mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
First commit
Former-commit-id: 9340873f9cfb15264004c32d6e4b8f8bd6828d94 Former-commit-id: 1916747c109876aa064412e01204c3aeda9bbbc0
This commit is contained in:
48
src/com/Torvald/ColourUtil/Col12.java
Normal file
48
src/com/Torvald/ColourUtil/Col12.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.Torvald.ColourUtil;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-23.
|
||||
*/
|
||||
public class Col12 {
|
||||
|
||||
private short data;
|
||||
|
||||
/**
|
||||
* Create new Col12 format
|
||||
* @param data 0x000-0xFFF, in RGB
|
||||
*/
|
||||
public Col12(int data) {
|
||||
this.data = (short) data;
|
||||
}
|
||||
|
||||
public Color create(int i) {
|
||||
if (i > 0xFFF || i < 0) {
|
||||
throw new IllegalArgumentException("Colour range: #000 - #FFF");
|
||||
}
|
||||
int r = (i & 0xF00) >> 8;
|
||||
int g = (i & 0x0F0) >> 4;
|
||||
int b = i & 0x00F;
|
||||
|
||||
return new Color(
|
||||
(r << 4) | r
|
||||
, (g << 4) | g
|
||||
, (b << 4) | b
|
||||
);
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
byte[] ret = new byte[3];
|
||||
int r = (data & 0xF00) >> 8;
|
||||
int g = (data & 0x0F0) >> 4;
|
||||
int b = data & 0x00F;
|
||||
|
||||
ret[0] = (byte) ((r << 4) | r);
|
||||
ret[1] = (byte) ((g << 4) | g);
|
||||
ret[2] = (byte) ((b << 4) | b);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
76
src/com/Torvald/ColourUtil/HSV.java
Normal file
76
src/com/Torvald/ColourUtil/HSV.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package com.Torvald.ColourUtil;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import org.newdawn.slick.Color;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-16.
|
||||
*/
|
||||
public class HSV {
|
||||
|
||||
/**
|
||||
* Convert HSV parameters to RGB color.
|
||||
* @param h 0-359 Hue
|
||||
* @param s 0-255 Saturation
|
||||
* @param v 0-255 Value
|
||||
* @return org.newdawn.slick.Color
|
||||
* @link http://www.rapidtables.com/convert/color/hsv-to-rgb.htm
|
||||
*/
|
||||
public static Color toRGB(int h, int s, int v) {
|
||||
int H = h;
|
||||
if (H < 0 || H >= 360) {
|
||||
H %= 360;
|
||||
}
|
||||
|
||||
float S = s / 255f;
|
||||
float V = v / 255f;
|
||||
|
||||
float C = V * S;
|
||||
float X = C * (1 - FastMath.abs(
|
||||
(H / 60f) % 2 - 1
|
||||
));
|
||||
float m = V - C;
|
||||
|
||||
float R_prime = Float.NaN;
|
||||
float G_prime = Float.NaN;
|
||||
float B_prime = Float.NaN;
|
||||
|
||||
if (H < 60) {
|
||||
R_prime = C;
|
||||
G_prime = X;
|
||||
B_prime = 0;
|
||||
}
|
||||
else if (H < 120) {
|
||||
R_prime = X;
|
||||
G_prime = C;
|
||||
B_prime = 0;
|
||||
}
|
||||
else if (H < 180) {
|
||||
R_prime = 0;
|
||||
G_prime = C;
|
||||
B_prime = X;
|
||||
}
|
||||
else if (H < 240) {
|
||||
R_prime = 0;
|
||||
G_prime = X;
|
||||
B_prime = C;
|
||||
}
|
||||
else if (H < 300) {
|
||||
R_prime = X;
|
||||
G_prime = 0;
|
||||
B_prime = C;
|
||||
}
|
||||
else if (H < 360) {
|
||||
R_prime = C;
|
||||
G_prime = 0;
|
||||
B_prime = X;
|
||||
}
|
||||
|
||||
return new Color(
|
||||
(int) ((R_prime + m) * 255)
|
||||
, (int) ((G_prime + m) * 255)
|
||||
, (int) ((B_prime + m) * 255)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
216
src/com/Torvald/ImageFont/GameFontBase.java
Normal file
216
src/com/Torvald/ImageFont/GameFontBase.java
Normal file
@@ -0,0 +1,216 @@
|
||||
package com.Torvald.ImageFont;
|
||||
|
||||
import com.Torvald.Terrarum.MapDrawer.LightmapRenderer;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.newdawn.slick.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-27.
|
||||
*/
|
||||
public class GameFontBase implements Font {
|
||||
|
||||
static SpriteSheet hangulSheet;
|
||||
static SpriteSheet asciiSheet;
|
||||
static SpriteSheet asciiSheetEF;
|
||||
static SpriteSheet runicSheet;
|
||||
|
||||
static final int JUNG_COUNT = 21;
|
||||
static final int JONG_COUNT = 28;
|
||||
|
||||
static final int W_CJK = 10;
|
||||
static final int W_CJK_DRAW = 11;
|
||||
static final int W_EM = 9; // width of regular letters, including m
|
||||
static final int W_EF = 5; // width of letter f, t, i, l
|
||||
static final int H = 20;
|
||||
static final int H_CJK = 16;
|
||||
|
||||
static final int SHEET_EM = 0;
|
||||
static final int SHEET_EF = 1;
|
||||
static final int SHEET_HANGUL = 3;
|
||||
static final int SHEET_CJKPUNCT = 4;
|
||||
static final int SHEET_KANA = 5;
|
||||
static final int SHEET_RUNIC = 6;
|
||||
|
||||
static SpriteSheet[] sheetKey;
|
||||
static final Character[] EFlist = {
|
||||
' ','!','"','\'',',','.','(',')',':',';','I','[',']','`','f','i'
|
||||
,'j','l','t','{','|','}'
|
||||
};
|
||||
|
||||
/**
|
||||
* Runic letters list used for game. The set is
|
||||
* Younger Futhark + Medieval rune 'e' + Punct + Runic Almanac
|
||||
*
|
||||
* Examples:
|
||||
* ᛭ᛋᛁᚴᚱᛁᚦᛦ᛭
|
||||
* ᛭ᛂᛚᛋᛅ᛭ᛏᚱᚢᛏᚾᛁᚾᚴᚢᚾᛅ᛬ᛅᚱᚾᛅᛏᛅᛚᛋ
|
||||
*/
|
||||
static final Character[] runicList = {
|
||||
'ᚠ','ᚢ','ᚦ','ᚯ','ᚱ','ᚴ','ᚼ','ᚾ','ᛁ','ᛅ','ᛋ','ᛏ','ᛒ','ᛘ','ᛚ','ᛦ'
|
||||
,'ᛂ','᛬','᛫','᛭','ᛮ','ᛯ','ᛰ'
|
||||
};
|
||||
|
||||
|
||||
public GameFontBase() throws SlickException {
|
||||
|
||||
}
|
||||
|
||||
private int[] getHan(int hanIndex) {
|
||||
int han_x = hanIndex % JONG_COUNT;
|
||||
int han_y = hanIndex / JONG_COUNT;
|
||||
int[] ret = {han_x, han_y};
|
||||
return ret;
|
||||
}
|
||||
|
||||
private boolean isEF(char c) {
|
||||
return (Arrays.asList(EFlist).contains(c));
|
||||
}
|
||||
|
||||
private boolean isHangul(char c) {
|
||||
return (c >= 0xAC00 && c < 0xD7A4);
|
||||
}
|
||||
|
||||
private boolean isAscii(char c) { return (c > 0 && c <= 0xFF); }
|
||||
|
||||
private boolean isRunic(char c) {
|
||||
return (Arrays.asList(runicList).contains(c));
|
||||
}
|
||||
|
||||
private int EFindexX(char c) {
|
||||
return (Arrays.asList(EFlist).indexOf(c) % 16);
|
||||
}
|
||||
|
||||
private int EFindexY(char c) {
|
||||
return (Arrays.asList(EFlist).indexOf(c) / 16);
|
||||
}
|
||||
|
||||
private int runicIndexX(char c) {
|
||||
return (Arrays.asList(runicList).indexOf(c) % 16);
|
||||
}
|
||||
|
||||
private int runicIndexY(char c) {
|
||||
return (Arrays.asList(runicList).indexOf(c) / 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(String s) {
|
||||
int len = 0;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
switch (getSheetType(c)) {
|
||||
case SHEET_EF:
|
||||
len += W_EF; break;
|
||||
case SHEET_HANGUL:
|
||||
len += W_CJK_DRAW; break;
|
||||
default:
|
||||
len += W_EM;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(String s) {
|
||||
return H;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineHeight() {
|
||||
return H;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawString(float v, float v1, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawString(float x, float y, String s, Color color) {
|
||||
// hangul fonts first
|
||||
hangulSheet.startUse();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char ch = s.charAt(i);
|
||||
|
||||
if (isHangul(ch)) {
|
||||
int[] hanPos = getHan(ch - 0xAC00);
|
||||
hangulSheet.renderInUse(
|
||||
Math.round(x
|
||||
+ getWidth(s.substring(0, i))
|
||||
)
|
||||
, Math.round((H - H_CJK) + y)
|
||||
, hanPos[0]
|
||||
, hanPos[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
hangulSheet.endUse();
|
||||
|
||||
//ascii fonts
|
||||
int prevInstance = -1;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char ch = s.charAt(i);
|
||||
|
||||
if (isAscii(ch)) {
|
||||
|
||||
// if not init, enduse first
|
||||
if (prevInstance != -1) {
|
||||
sheetKey[prevInstance].endUse();
|
||||
}
|
||||
sheetKey[getSheetType(ch)].startUse();
|
||||
prevInstance = getSheetType(ch);
|
||||
|
||||
int sheetX;
|
||||
int sheetY;
|
||||
switch (prevInstance) {
|
||||
case SHEET_EF:
|
||||
sheetX = EFindexX(ch);
|
||||
sheetY = EFindexY(ch);
|
||||
break;
|
||||
case SHEET_EM:
|
||||
default:
|
||||
sheetX = ch % 16;
|
||||
sheetY = ch / 16;
|
||||
break;
|
||||
}
|
||||
|
||||
sheetKey[prevInstance].renderInUse(
|
||||
Math.round(x
|
||||
+ getWidth(s.substring(0, i))
|
||||
)
|
||||
, Math.round(y)
|
||||
, sheetX
|
||||
, sheetY
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (prevInstance != -1) {
|
||||
sheetKey[prevInstance].endUse();
|
||||
}
|
||||
}
|
||||
|
||||
private int getSheetType(char c) {
|
||||
if (isEF(c)) {
|
||||
return SHEET_EF;
|
||||
}
|
||||
else if (isHangul(c)) {
|
||||
return SHEET_HANGUL;
|
||||
}
|
||||
else if (isRunic(c)) {
|
||||
return SHEET_RUNIC;
|
||||
}
|
||||
else {
|
||||
return SHEET_EM;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawString(float v, float v1, String s, Color color, int i, int i1) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
37
src/com/Torvald/ImageFont/GameFontBlack.java
Normal file
37
src/com/Torvald/ImageFont/GameFontBlack.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.Torvald.ImageFont;
|
||||
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.SpriteSheet;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-27.
|
||||
*/
|
||||
public class GameFontBlack extends GameFontBase {
|
||||
|
||||
public GameFontBlack() throws SlickException {
|
||||
super();
|
||||
|
||||
hangulSheet = new SpriteSheet(
|
||||
"./res/graphics/fonts/han_atlas_black.png"
|
||||
, W_CJK
|
||||
, H_CJK
|
||||
);
|
||||
asciiSheet = new SpriteSheet(
|
||||
"./res/graphics/fonts/ascii_majuscule_black.png"
|
||||
, W_EM
|
||||
, H
|
||||
);
|
||||
asciiSheetEF = new SpriteSheet(
|
||||
"./res/graphics/fonts/ascii_special_ef_black.png"
|
||||
, W_EF
|
||||
, H
|
||||
);
|
||||
|
||||
SpriteSheet[] shk = {
|
||||
asciiSheet
|
||||
, asciiSheetEF
|
||||
, hangulSheet
|
||||
};
|
||||
sheetKey = shk;
|
||||
}
|
||||
}
|
||||
42
src/com/Torvald/ImageFont/GameFontWhite.java
Normal file
42
src/com/Torvald/ImageFont/GameFontWhite.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.Torvald.ImageFont;
|
||||
|
||||
import com.Torvald.Terrarum.MapDrawer.LightmapRenderer;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.newdawn.slick.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-20.
|
||||
*/
|
||||
public class GameFontWhite extends GameFontBase {
|
||||
|
||||
public GameFontWhite() throws SlickException {
|
||||
super();
|
||||
|
||||
hangulSheet = new SpriteSheet(
|
||||
"./res/graphics/fonts/han_atlas.png"
|
||||
, W_CJK
|
||||
, H_CJK
|
||||
);
|
||||
asciiSheet = new SpriteSheet(
|
||||
"./res/graphics/fonts/ascii_majuscule.png"
|
||||
, W_EM
|
||||
, H
|
||||
);
|
||||
asciiSheetEF = new SpriteSheet(
|
||||
"./res/graphics/fonts/ascii_special_ef.png"
|
||||
, W_EF
|
||||
, H
|
||||
);
|
||||
|
||||
SpriteSheet[] shk = {
|
||||
asciiSheet
|
||||
, asciiSheetEF
|
||||
, hangulSheet
|
||||
};
|
||||
sheetKey = shk;
|
||||
}
|
||||
|
||||
}
|
||||
28
src/com/Torvald/Point/Point2f.java
Normal file
28
src/com/Torvald/Point/Point2f.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.Torvald.Point;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
public class Point2f {
|
||||
|
||||
private float x;
|
||||
private float y;
|
||||
|
||||
public Point2f(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void set(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
14
src/com/Torvald/Rand/Fudge3.java
Normal file
14
src/com/Torvald/Rand/Fudge3.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.Torvald.Rand;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-03.
|
||||
*/
|
||||
public class Fudge3 {
|
||||
|
||||
public FudgeDice create(Random rand) {
|
||||
return new FudgeDice(rand, 3);
|
||||
}
|
||||
|
||||
}
|
||||
42
src/com/Torvald/Rand/FudgeDice.java
Normal file
42
src/com/Torvald/Rand/FudgeDice.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.Torvald.Rand;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-03.
|
||||
*/
|
||||
public class FudgeDice {
|
||||
|
||||
private Random randfunc;
|
||||
private int diceCounts;
|
||||
|
||||
/**
|
||||
* Define new set of fudge dice with given counts.
|
||||
* @param randfunc
|
||||
* @param counts amount of die
|
||||
*/
|
||||
public FudgeDice(Random randfunc, int counts) {
|
||||
this.randfunc = randfunc;
|
||||
diceCounts = counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll dice and get result. Range: [-3, 3] for three dice
|
||||
* @return
|
||||
*/
|
||||
public int roll() {
|
||||
int diceResult = 0;
|
||||
for (int c = 0; c < diceCounts; c++) {
|
||||
diceResult += rollSingleDie();
|
||||
}
|
||||
|
||||
return diceResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return random [-1, 0, 1]
|
||||
*/
|
||||
private int rollSingleDie() {
|
||||
return (randfunc.nextInt(3)) - 1;
|
||||
}
|
||||
}
|
||||
59
src/com/Torvald/Rand/HighQualityRandom.java
Normal file
59
src/com/Torvald/Rand/HighQualityRandom.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package com.Torvald.Rand;
|
||||
|
||||
import java.util.Random;
|
||||
//import java.util.concurrent.locks.*;
|
||||
|
||||
/**
|
||||
* This class implements a better random number generator than the standard LCG that is implemented in java.util.Random.
|
||||
* It is based on <a href="http://www.amazon.com/gp/product/0521880688?ie=UTF8&tag=javamex-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0521880688">Numerical Recipes: The Art of Scientific Computing</a>,
|
||||
* and gives a good compromise between quality and speed. It is a combined generator: two XORShift generators are combined with an LCG and a multiply with carry generator.
|
||||
* (Without going into all the details here, notice the two blocks of three shifts each, which are the XORShifts; the first line which is the LCG, similar to the standard
|
||||
* Java Random algorithm, and the line between the two XORShifts, which is a multiply with carry generator.)
|
||||
* Note that this version is <b>not</b> thread-safe. In order to make it thread-safe, uncomment the lock-related lines. It is also <b>not</b> cryptographically secure, like the java.security.SecureRandom class.
|
||||
* @author Numerical Recipes
|
||||
*/
|
||||
|
||||
public class HighQualityRandom extends Random {
|
||||
|
||||
//private Lock l = new ReentrantLock();
|
||||
private long u;
|
||||
private long v = 4101842887655102017L;
|
||||
private long w = 1;
|
||||
|
||||
public HighQualityRandom() {
|
||||
this(System.nanoTime());
|
||||
}
|
||||
public HighQualityRandom(long seed) {
|
||||
//l.lock();
|
||||
u = seed ^ v;
|
||||
nextLong();
|
||||
v = u;
|
||||
nextLong();
|
||||
w = v;
|
||||
nextLong();
|
||||
//l.unlock();
|
||||
}
|
||||
|
||||
public long nextLong() {
|
||||
// l.lock();
|
||||
try {
|
||||
u = u * 2862933555777941757L + 7046029254386353087L;
|
||||
v ^= v >>> 17;
|
||||
v ^= v << 31;
|
||||
v ^= v >>> 8;
|
||||
w = 4294957665L * (w & 0xffffffff) + (w >>> 32);
|
||||
long x = u ^ (u << 21);
|
||||
x ^= x >>> 35;
|
||||
x ^= x << 4;
|
||||
long ret = (x + v) ^ w;
|
||||
return ret;
|
||||
} finally {
|
||||
//l.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected int next(int bits) {
|
||||
return (int) (nextLong() >>> (64-bits));
|
||||
}
|
||||
|
||||
}
|
||||
1440
src/com/Torvald/Rand/MTRandom.java
Normal file
1440
src/com/Torvald/Rand/MTRandom.java
Normal file
File diff suppressed because it is too large
Load Diff
139
src/com/Torvald/Terrarum/ABOUT
Normal file
139
src/com/Torvald/Terrarum/ABOUT
Normal file
@@ -0,0 +1,139 @@
|
||||
== CHALLENGING, NOT PUNISHING https://www.youtube.com/watch?v=ea6UuRTjkKs
|
||||
|
||||
1. CONSISTENT RULES
|
||||
- No arbitrary unstoppable death
|
||||
|
||||
2. Player's skill involved
|
||||
- Can play around, not restart
|
||||
|
||||
3. Usability of in-game tools
|
||||
- Players should be able to 'regret' their strategy and adjust.
|
||||
|
||||
4. Comfortable control
|
||||
|
||||
5. Make players overcome the challenge, not defeating them
|
||||
|
||||
6. Let players have "aha" moment when they failed.
|
||||
- Make them hungry to retry with new strategies.
|
||||
- Some small things they've could done differently
|
||||
- e.g. "One-big-hit didn't worked, may I should've picked up high DPS one"
|
||||
|
||||
|
||||
== MORE DEPTH, LESS COMPLEXITY https://www.youtube.com/watch?v=jVL4st0blGU
|
||||
|
||||
1. Memorise less!
|
||||
- Less burden to, even starting the game
|
||||
- Start with gentle learning curve, getting slowly steep
|
||||
- Intuitive UX (UI, control, ...)
|
||||
- Good tutorial = lessens complexity
|
||||
|
||||
2. Intuitive!
|
||||
|
||||
3. Calculations per second
|
||||
- reduce!
|
||||
|
||||
4. Players have to know everything to even begin the play == FAIL (irreducible complexity)
|
||||
- Make them get familiar with rules of the game
|
||||
- Dwarf Fortress failed this!
|
||||
|
||||
|
||||
== Lots of things players play with (aka don't make them bored)
|
||||
- Combat, battle, building, mechanics, adventure, dungeon explore, spelunking
|
||||
- Not scaled; easy combat, tough combat, tedious combat, etc.
|
||||
|
||||
|
||||
== Achieving perfect imbalance https://www.youtube.com/watch?v=e31OSVZF77w
|
||||
- Make sure no matter how you skilled, your playable character cannot be good at everything
|
||||
- Give players __wide pool of options__ to solve problem
|
||||
(kill the boss, defend their adobe, fast transportation, etc.)
|
||||
|
||||
|
||||
|
||||
====================================
|
||||
|
||||
|
||||
* Friendlier version of Dwarf Fortress Adventure mode
|
||||
- Yet _lots of fun_
|
||||
- Add Fortress mode features by 'make your own settling'
|
||||
- Hard to actually die, but once you die, you're done.
|
||||
+ Config: imtooyoungtodie for easy mode
|
||||
|
||||
|
||||
|
||||
* Side view
|
||||
|
||||
* Interact menu w/ mouse right
|
||||
|
||||
* Pixelated sprites
|
||||
- Use 2x sprites if rotating does not work well
|
||||
|
||||
|
||||
|
||||
### User experience ###
|
||||
|
||||
* Indicative mouse cursor
|
||||
|
||||
|
||||
|
||||
### Game mechanics ###
|
||||
|
||||
* 24 pixels == 1 metre
|
||||
|
||||
|
||||
|
||||
### Purpose of the game ###
|
||||
|
||||
* Boss
|
||||
- Will be mentioned/shown as absolute _evil_.
|
||||
- But actually is not.
|
||||
|
||||
* Theme
|
||||
- Is an evil really really is what we think?
|
||||
- Is there a thing as 'absolute evil'?
|
||||
|
||||
* Boss character
|
||||
- From debugger character
|
||||
- Name key: "Sigriðr hinn Dróttningin" (can be changed)
|
||||
* Little setting
|
||||
- A ruler, hated by people
|
||||
|
||||
* Mechanics
|
||||
- Beating boss does not ends the game, but grants an ability to
|
||||
create new character as it.
|
||||
|
||||
|
||||
|
||||
### Making sprite ###
|
||||
|
||||
* Layers
|
||||
- (Optional) Hair foreground
|
||||
- Right arm dress
|
||||
- Right arm body
|
||||
- Dress
|
||||
- Boot right
|
||||
- Boot left
|
||||
- Body
|
||||
- (Optional) Hair accessory
|
||||
- Hair
|
||||
- Head
|
||||
- Left arm dress
|
||||
- Left arm body
|
||||
- (Optional) SFX
|
||||
|
||||
* Size
|
||||
- Regular sprite 'height': 40 px
|
||||
- Apparent height may vary
|
||||
|
||||
|
||||
|
||||
### Chargen ###
|
||||
|
||||
* Select hair, colours, then compile them into single spritesheet
|
||||
|
||||
* NO gender distinction, but have masculine/neutral/feminine designs (in clothing, hairstyles, etc.)
|
||||
|
||||
* Colour: 4096 colours (12-bit 0x000 - 0xFFF)
|
||||
|
||||
* Height variation option (.85 - .90 - .95 - 1 - 1.05 - 1.10 - 1.15)
|
||||
|
||||
* Base mass: 60 kg
|
||||
10
src/com/Torvald/Terrarum/Actors/AIControlled.java
Normal file
10
src/com/Torvald/Terrarum/Actors/AIControlled.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-31.
|
||||
*/
|
||||
public interface AIControlled {
|
||||
|
||||
void attachAI();
|
||||
|
||||
}
|
||||
14
src/com/Torvald/Terrarum/Actors/Actor.java
Normal file
14
src/com/Torvald/Terrarum/Actors/Actor.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public interface Actor {
|
||||
|
||||
void update(GameContainer gc, int delta_t);
|
||||
|
||||
long getRefID();
|
||||
}
|
||||
107
src/com/Torvald/Terrarum/Actors/ActorInventory.java
Normal file
107
src/com/Torvald/Terrarum/Actors/ActorInventory.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.Terrarum.GameItem.InventoryItem;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
public class ActorInventory {
|
||||
|
||||
@Nullable private int capacityByCount;
|
||||
@Nullable private int capacityByWeight;
|
||||
private int capacityMode;
|
||||
|
||||
private LinkedList<InventoryItem> pocket;
|
||||
|
||||
public final int CAPACITY_MODE_COUNT = 1;
|
||||
public final int CAPACITY_MODE_WEIGHT = 2;
|
||||
|
||||
/**
|
||||
* Construct new inventory with specified capacity.
|
||||
* @param capacity if is_weight is true, killogramme value is required, counts of items otherwise.
|
||||
* @param is_weight
|
||||
*/
|
||||
public ActorInventory(int capacity, boolean is_weight) {
|
||||
if (is_weight) {
|
||||
capacityByWeight = capacity;
|
||||
capacityMode = CAPACITY_MODE_WEIGHT;
|
||||
}
|
||||
else{
|
||||
capacityByCount = capacity;
|
||||
capacityMode = CAPACITY_MODE_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capacity of inventory
|
||||
* @return
|
||||
*/
|
||||
public int getCapacity() {
|
||||
if (capacityMode == CAPACITY_MODE_WEIGHT) {
|
||||
return capacityByWeight;
|
||||
}
|
||||
else {
|
||||
return capacityByCount;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCapacityMode() {
|
||||
return capacityMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reference to the pocket
|
||||
* @return
|
||||
*/
|
||||
public LinkedList<InventoryItem> getPocket() {
|
||||
return pocket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get clone of the pocket
|
||||
* @return
|
||||
*/
|
||||
public LinkedList<InventoryItem> getCopyOfPocket() {
|
||||
return (LinkedList<InventoryItem>) (pocket.clone());
|
||||
}
|
||||
|
||||
public float getTotalWeight() {
|
||||
float weight = 0;
|
||||
|
||||
for (InventoryItem item : pocket) {
|
||||
weight += item.getWeight();
|
||||
}
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
public float getTotalCount() {
|
||||
int count = 0;
|
||||
|
||||
for (InventoryItem item : pocket) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void appendToPocket(InventoryItem item) {
|
||||
pocket.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the pocket contains too many items
|
||||
* @return
|
||||
*/
|
||||
public boolean isEncumbered() {
|
||||
if (getCapacityMode() == CAPACITY_MODE_WEIGHT) {
|
||||
return (capacityByWeight < getTotalWeight());
|
||||
}
|
||||
else {
|
||||
return (capacityByCount < getTotalWeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/com/Torvald/Terrarum/Actors/ActorValue.java
Normal file
41
src/com/Torvald/Terrarum/Actors/ActorValue.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-03.
|
||||
*/
|
||||
public class ActorValue {
|
||||
|
||||
private Hashtable<String, Object> configTable;
|
||||
|
||||
public ActorValue() {
|
||||
configTable = new Hashtable<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add key-value pair to the configuration table.
|
||||
*
|
||||
* @param key case insensitive
|
||||
* @param value
|
||||
*/
|
||||
public void set(String key, Object value){
|
||||
configTable.put(key.toLowerCase(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value using key from configuration table.
|
||||
*
|
||||
* @param key case insensitive
|
||||
* @return Object value
|
||||
*/
|
||||
public Object get(String key){
|
||||
return configTable.get(key.toLowerCase());
|
||||
}
|
||||
|
||||
public Set getKeySet() {
|
||||
return configTable.keySet();
|
||||
}
|
||||
|
||||
}
|
||||
453
src/com/Torvald/Terrarum/Actors/ActorWithBody.java
Normal file
453
src/com/Torvald/Terrarum/Actors/ActorWithBody.java
Normal file
@@ -0,0 +1,453 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.Rand.HighQualityRandom;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.MapDrawer.MapDrawer;
|
||||
import com.Torvald.spriteAnimation.SpriteAnimation;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-13.
|
||||
*/
|
||||
public class ActorWithBody implements Actor, Visible, Glowing {
|
||||
|
||||
private @NotNull float hitboxTranslateX; // relative to spritePosX
|
||||
private @NotNull float hitboxTranslateY; // relative to spritePosY
|
||||
private @NotNull int baseHitboxW;
|
||||
private @NotNull int baseHitboxH;
|
||||
|
||||
/**
|
||||
* Velocity for newtonian sim.
|
||||
* Fluctuation in, otherwise still, velocity is equal to acceleration.
|
||||
*
|
||||
* Acceleration: used in code like:
|
||||
* veloY += 3.0
|
||||
* +3.0 is acceleration. You __accumulate__ acceleration to the velocity.
|
||||
*/
|
||||
private @NotNull float veloX, veloY, veloMax;
|
||||
|
||||
|
||||
private boolean grounded = false;
|
||||
|
||||
SpriteAnimation sprite;
|
||||
@Nullable SpriteAnimation spriteGlow;
|
||||
private boolean visible = false;
|
||||
private boolean update = true;
|
||||
|
||||
@NotNull int baseSpriteWidth, baseSpriteHeight;
|
||||
|
||||
/**
|
||||
* Positions: top-left point
|
||||
*/
|
||||
private @NotNull Hitbox hitbox, nextHitbox;
|
||||
|
||||
/**
|
||||
* Physical properties
|
||||
*/
|
||||
private float scale = 1;
|
||||
private float mass = 1f;
|
||||
|
||||
private static int TSIZE = MapDrawer.TILE_SIZE;
|
||||
|
||||
/**
|
||||
* Gravitational Constant G. Load from GameMap.
|
||||
* [m / s^2]
|
||||
* s^2 = 1/FPS = 1/60 if FPS is targeted to 60
|
||||
* meter to pixel : 24/FPS
|
||||
*/
|
||||
private final float METER = 24f;
|
||||
private final float SI_TO_GAME_ACC = METER / (Terrarum.TARGET_FPS * Terrarum.TARGET_FPS);
|
||||
private final float SI_TO_GAME_VEL = METER / Terrarum.TARGET_FPS;
|
||||
private float gravitation;
|
||||
private final float DRAG_COEFF = 1f;
|
||||
|
||||
/**
|
||||
* A constant to make falling faster so that the game is more playable
|
||||
*/
|
||||
private final float G_MUL_PLAYABLE_CONST = 1.4142f;
|
||||
|
||||
long referenceID;
|
||||
|
||||
public ActorWithBody() {
|
||||
referenceID = new HighQualityRandom(0x7E22A211AAL).nextLong();
|
||||
}
|
||||
|
||||
public void setHitboxDimension(int w, int h, int tx, int ty) {
|
||||
baseHitboxH = h;
|
||||
baseHitboxW = w;
|
||||
hitboxTranslateX = tx;
|
||||
hitboxTranslateY = ty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set hitbox position from bottom-center point
|
||||
* @param x
|
||||
* @param y
|
||||
*/
|
||||
public void setPosition(float x, float y) {
|
||||
hitbox = new Hitbox(
|
||||
x - ((baseHitboxW / 2) - hitboxTranslateX) * scale
|
||||
, y - (baseHitboxH - hitboxTranslateY) * scale
|
||||
, baseHitboxW * scale
|
||||
, baseHitboxH * scale
|
||||
);
|
||||
|
||||
nextHitbox = new Hitbox(
|
||||
x - ((baseHitboxW / 2) - hitboxTranslateX) * scale
|
||||
, y - (baseHitboxH - hitboxTranslateY) * scale
|
||||
, baseHitboxW * scale
|
||||
, baseHitboxH * scale
|
||||
);
|
||||
}
|
||||
|
||||
public void setSprite(SpriteAnimation sprite) {this.sprite = sprite; }
|
||||
|
||||
public void setSpriteGlow(SpriteAnimation sprite) { this.spriteGlow = sprite; }
|
||||
|
||||
public void update(GameContainer gc, int delta_t) {
|
||||
if (update) {
|
||||
/**
|
||||
* Update variables
|
||||
*/
|
||||
baseSpriteHeight = sprite.getHeight();
|
||||
baseSpriteWidth = sprite.getWidth();
|
||||
gravitation = Game.map.getGravitation();
|
||||
|
||||
applyGravitation();
|
||||
|
||||
//Set 'next' positions to fiddle with
|
||||
updateNextHitbox(delta_t);
|
||||
|
||||
updateVerticalPos();
|
||||
updateHorizontalPos();
|
||||
|
||||
// Apply previous fiddling
|
||||
updateHitbox();
|
||||
|
||||
|
||||
/**
|
||||
* clamp position
|
||||
*/
|
||||
hitbox.setPositionFromPoint(
|
||||
clampW(hitbox.getPointedX())
|
||||
, clampH(hitbox.getPointedY())
|
||||
);
|
||||
nextHitbox.setPositionFromPoint(
|
||||
clampW(nextHitbox.getPointedX())
|
||||
, clampH(nextHitbox.getPointedY())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGlow(GameContainer gc, Graphics g) {
|
||||
if (visible && spriteGlow != null) {
|
||||
if (!sprite.flippedHorizontal()) {
|
||||
spriteGlow.render(g
|
||||
, Math.round(hitbox.getPosX() - (hitboxTranslateX * scale))
|
||||
, Math.round(hitbox.getPosY() - hitboxTranslateY * scale)
|
||||
- (baseSpriteHeight - baseHitboxH) * scale
|
||||
+ 1
|
||||
, scale
|
||||
);
|
||||
}
|
||||
else {
|
||||
spriteGlow.render(g
|
||||
, Math.round(hitbox.getPosX() - scale)
|
||||
, Math.round(hitbox.getPosY() - hitboxTranslateY * scale)
|
||||
- (baseSpriteHeight - baseHitboxH) * scale
|
||||
+ 1
|
||||
, scale
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBody(GameContainer gc, Graphics g) {
|
||||
if (visible) {
|
||||
if (!sprite.flippedHorizontal()) {
|
||||
sprite.render(g
|
||||
, Math.round(hitbox.getPosX() - (hitboxTranslateX * scale))
|
||||
, Math.round(hitbox.getPosY() - hitboxTranslateY * scale)
|
||||
- (baseSpriteHeight - baseHitboxH) * scale
|
||||
+ 1
|
||||
, scale
|
||||
);
|
||||
}
|
||||
else {
|
||||
sprite.render(g
|
||||
, Math.round(hitbox.getPosX() - scale)
|
||||
, Math.round(hitbox.getPosY() - hitboxTranslateY * scale)
|
||||
- (baseSpriteHeight - baseHitboxH) * scale
|
||||
+ 1
|
||||
, scale
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGlowSprite(GameContainer gc, int delta_t) {
|
||||
if (spriteGlow != null) {
|
||||
spriteGlow.update(delta_t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBodySprite(GameContainer gc, int delta_t) {
|
||||
sprite.update(delta_t);
|
||||
}
|
||||
|
||||
boolean collideBottomAndAdjust() {
|
||||
// noclip off?
|
||||
if (!(this instanceof Player && ((Player) this).isNoClip())) {
|
||||
int feetTileX = clampWtile(Math.round((nextHitbox.getPointedX()) / TSIZE));
|
||||
int feetTileY = clampHtile(FastMath.floor(nextHitbox.getPointedY() / TSIZE));
|
||||
|
||||
if (feetTileX < 0) feetTileX = 0;
|
||||
if (feetTileY < 0) feetTileY = 0;
|
||||
|
||||
int feetTile = Game.map.getTileFromTerrain(feetTileX, feetTileY);
|
||||
|
||||
if (feetTile != 0) {
|
||||
nextHitbox.setPositionYFromPoint(
|
||||
feetTileY * TSIZE
|
||||
);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply gravitation to the every falling body (unless not levitating)
|
||||
*
|
||||
* Apply only if not grounded; normal force is not implemented (and redundant)
|
||||
* so we manually reset G to zero (not applying G. force) if grounded.
|
||||
*/
|
||||
private void applyGravitation() {
|
||||
if (!isGrounded()) {
|
||||
/**
|
||||
* weight; gravitational force in action
|
||||
* W = mass * G (9.8 [m/s^2])
|
||||
*/
|
||||
float W = gravitation * mass;
|
||||
/**
|
||||
* Drag of atmosphere
|
||||
* D = Cd (drag coefficient) * 0.5 * rho (density) * V^2 (velocity) * A (area)
|
||||
*/
|
||||
float A = scale * scale;
|
||||
float D = DRAG_COEFF * 0.5f * 1.292f * veloY * veloY * A;
|
||||
|
||||
veloY += ((W - D) / mass) * SI_TO_GAME_ACC * G_MUL_PLAYABLE_CONST;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateVerticalPos() {
|
||||
if (!playerNoClip()) {
|
||||
if (collideBottomAndAdjust()) {
|
||||
grounded = true;
|
||||
veloY = 0;
|
||||
}
|
||||
else {
|
||||
grounded = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
grounded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHorizontalPos() {
|
||||
|
||||
}
|
||||
|
||||
private void updateNextHitbox(int delta_t) {
|
||||
nextHitbox.set(
|
||||
hitbox.getPosX() + veloX
|
||||
, hitbox.getPosY() + veloY
|
||||
, baseHitboxW * scale
|
||||
, baseHitboxH * scale
|
||||
);
|
||||
}
|
||||
|
||||
private void updateHitbox() {
|
||||
hitbox.set(
|
||||
nextHitbox.getPosX()
|
||||
, nextHitbox.getPosY()
|
||||
, baseHitboxW * scale
|
||||
, baseHitboxH * scale
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRefID() {
|
||||
return referenceID;
|
||||
}
|
||||
|
||||
public float pointedPosX() { return hitbox.getPointedX(); }
|
||||
public float pointedPosY() { return hitbox.getPointedY(); }
|
||||
public float topLeftPosX() { return hitbox.getPosX(); }
|
||||
public float topLeftPosY() { return hitbox.getPosY(); }
|
||||
|
||||
private static float clampW(float x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x >= Game.map.width * TSIZE) {
|
||||
return Game.map.width * TSIZE - 1;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static float clampH(float x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x >= Game.map.height * TSIZE) {
|
||||
return Game.map.height * TSIZE - 1;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clampWtile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x >= Game.map.width) {
|
||||
return Game.map.width - 1;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clampHtile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x >= Game.map.height) {
|
||||
return Game.map.height - 1;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean playerNoClip() {
|
||||
return (this instanceof Player && ((Player) this).isNoClip());
|
||||
}
|
||||
|
||||
private static int div16(int x) {
|
||||
if (x < 0) { throw new IllegalArgumentException("Positive integer only!"); }
|
||||
return (x & 0x7FFF_FFFF) >> 4;
|
||||
}
|
||||
|
||||
private static int mod16(int x) {
|
||||
if (x < 0) { throw new IllegalArgumentException("Positive integer only!"); }
|
||||
return x & 0b1111;
|
||||
}
|
||||
|
||||
public void setVisible(boolean visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public void setScale(float scale) {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public void setMass(float mass) {
|
||||
this.mass = mass;
|
||||
}
|
||||
|
||||
public void setVeloX(float veloX) {
|
||||
this.veloX = veloX;
|
||||
}
|
||||
|
||||
public void setVeloY(float veloY) {
|
||||
this.veloY = veloY;
|
||||
}
|
||||
|
||||
public void setVeloMax(float veloMax) {
|
||||
this.veloMax = veloMax;
|
||||
}
|
||||
|
||||
public void setGrounded(boolean grounded) {
|
||||
this.grounded = grounded;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
public float getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
public float getMass() {
|
||||
return mass;
|
||||
}
|
||||
|
||||
public float getVeloX() {
|
||||
return veloX;
|
||||
}
|
||||
|
||||
public float getVeloY() {
|
||||
return veloY;
|
||||
}
|
||||
|
||||
public float getVeloMax() {
|
||||
return veloMax;
|
||||
}
|
||||
|
||||
public boolean isGrounded() {
|
||||
return grounded;
|
||||
}
|
||||
|
||||
public int getBaseHitboxW() {
|
||||
return baseHitboxW;
|
||||
}
|
||||
|
||||
public int getBaseHitboxH() {
|
||||
return baseHitboxH;
|
||||
}
|
||||
|
||||
public float getHitboxTranslateX() {
|
||||
return hitboxTranslateX;
|
||||
}
|
||||
|
||||
public float getHitboxTranslateY() {
|
||||
return hitboxTranslateY;
|
||||
}
|
||||
|
||||
public Hitbox getHitbox() {
|
||||
return hitbox;
|
||||
}
|
||||
|
||||
public Hitbox getNextHitbox() {
|
||||
return nextHitbox;
|
||||
}
|
||||
|
||||
public boolean isUpdate() {
|
||||
return update;
|
||||
}
|
||||
|
||||
public void setUpdate(boolean update) {
|
||||
this.update = update;
|
||||
}
|
||||
}
|
||||
20
src/com/Torvald/Terrarum/Actors/CanBeStoredAsItem.java
Normal file
20
src/com/Torvald/Terrarum/Actors/CanBeStoredAsItem.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.Terrarum.GameItem.InventoryItem;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-31.
|
||||
*/
|
||||
public interface CanBeStoredAsItem {
|
||||
|
||||
void attachItemData();
|
||||
|
||||
float getItemWeight();
|
||||
|
||||
void stopUpdateAndDraw();
|
||||
|
||||
void resumeUpdateAndDraw();
|
||||
|
||||
InventoryItem getItemData();
|
||||
|
||||
}
|
||||
17
src/com/Torvald/Terrarum/Actors/Controllable.java
Normal file
17
src/com/Torvald/Terrarum/Actors/Controllable.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.spriteAnimation.SpriteAnimation;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public interface Controllable {
|
||||
|
||||
void processInput(Input input);
|
||||
|
||||
void keyPressed(int key, char c);
|
||||
|
||||
}
|
||||
15
src/com/Torvald/Terrarum/Actors/Glowing.java
Normal file
15
src/com/Torvald/Terrarum/Actors/Glowing.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
public interface Glowing {
|
||||
|
||||
void drawGlow(GameContainer gc, Graphics g);
|
||||
|
||||
void updateGlowSprite(GameContainer gc, int delta_t);
|
||||
|
||||
}
|
||||
112
src/com/Torvald/Terrarum/Actors/Hitbox.java
Normal file
112
src/com/Torvald/Terrarum/Actors/Hitbox.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.Point.Point2f;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
public class Hitbox {
|
||||
|
||||
private Point2f hitboxStart;
|
||||
private Point2f hitboxEnd;
|
||||
private float width;
|
||||
private float height;
|
||||
private float pointX;
|
||||
private float pointY;
|
||||
|
||||
public Hitbox(float x1, float y1, float width, float height) {
|
||||
hitboxStart = new Point2f(x1, y1);
|
||||
hitboxEnd = new Point2f(x1 + width, y1 + height);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
pointX = x1 + (width / 2);
|
||||
pointY = y1 + height;
|
||||
}
|
||||
|
||||
public Point2f getHitboxStart() {
|
||||
return hitboxStart;
|
||||
}
|
||||
|
||||
public Point2f getHitboxEnd() {
|
||||
return hitboxEnd;
|
||||
}
|
||||
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public float getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns bottom-centered point of hitbox.
|
||||
* @return pointX
|
||||
*/
|
||||
public float getPointedX() {
|
||||
return pointX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns bottom-centered point of hitbox.
|
||||
* @return pointY
|
||||
*/
|
||||
public float getPointedY() {
|
||||
return pointY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to the point top left
|
||||
* @param x1
|
||||
* @param y1
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public void set(float x1, float y1, float width, float height) {
|
||||
hitboxStart = new Point2f(x1, y1);
|
||||
hitboxEnd = new Point2f(x1 + width, y1 + height);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
pointX = x1 + (width / 2);
|
||||
pointY = y1 + height;
|
||||
}
|
||||
|
||||
public void setPositionFromPoint(float x1, float y1) {
|
||||
hitboxStart = new Point2f(x1 - (width / 2), y1 - height);
|
||||
hitboxEnd = new Point2f(hitboxStart.getX() + width, hitboxStart.getY() + height);
|
||||
pointX = x1;
|
||||
pointY = y1;
|
||||
}
|
||||
|
||||
public void setPositionXFromPoint(float x1) {
|
||||
float y1 = pointY;
|
||||
hitboxStart = new Point2f(x1 - (width / 2), y1 - height);
|
||||
hitboxEnd = new Point2f(hitboxStart.getX() + width, hitboxStart.getY() + height);
|
||||
pointX = x1;
|
||||
}
|
||||
|
||||
public void setPositionYFromPoint(float y1) {
|
||||
float x1 = pointX;
|
||||
hitboxStart = new Point2f(x1 - (width / 2), y1 - height);
|
||||
hitboxEnd = new Point2f(hitboxStart.getX() + width, hitboxStart.getY() + height);
|
||||
pointY = y1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns x value of start point
|
||||
* @return top-left point posX
|
||||
*/
|
||||
public float getPosX() {
|
||||
return hitboxStart.getX();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns y value of start point
|
||||
* @return top-left point posY
|
||||
*/
|
||||
public float getPosY() {
|
||||
return hitboxStart.getY();
|
||||
}
|
||||
}
|
||||
28
src/com/Torvald/Terrarum/Actors/ItemTangible.java
Normal file
28
src/com/Torvald/Terrarum/Actors/ItemTangible.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.spriteAnimation.SpriteAnimation;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-03.
|
||||
*/
|
||||
public class ItemTangible extends ActorWithBody {
|
||||
|
||||
@NotNull private float mass;
|
||||
|
||||
public ItemTangible() {
|
||||
super.setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer gc, int delta_t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBody(GameContainer gc, Graphics g) {
|
||||
super.drawBody(gc, g);
|
||||
}
|
||||
}
|
||||
64
src/com/Torvald/Terrarum/Actors/PBFSigrid.java
Normal file
64
src/com/Torvald/Terrarum/Actors/PBFSigrid.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.spriteAnimation.SpriteAnimation;
|
||||
import org.newdawn.slick.SlickException;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-03.
|
||||
*/
|
||||
public class PBFSigrid {
|
||||
|
||||
public Player build() throws SlickException {
|
||||
Player p = new Player();
|
||||
|
||||
p.referenceID = Game.PLAYER_REF_ID;
|
||||
|
||||
p.setVisible(true);
|
||||
|
||||
p.sprite = new SpriteAnimation();
|
||||
p.sprite.setDimension(28, 50);
|
||||
p.sprite.setSpriteImage("res/graphics/sprites/test_player.png");
|
||||
p.sprite.setDelay(200);
|
||||
p.sprite.setRowsAndFrames(1, 1);
|
||||
p.sprite.setAsVisible();
|
||||
p.sprite.composeSprite();
|
||||
|
||||
p.spriteGlow = new SpriteAnimation();
|
||||
p.spriteGlow.setDimension(28, 50);
|
||||
p.spriteGlow.setSpriteImage("res/graphics/sprites/test_player_glow.png");
|
||||
p.spriteGlow.setDelay(200);
|
||||
p.spriteGlow.setRowsAndFrames(1, 1);
|
||||
p.spriteGlow.setAsVisible();
|
||||
p.spriteGlow.composeSprite();
|
||||
|
||||
|
||||
p.actorValue = new ActorValue();
|
||||
p.actorValue.set("scale", 1.0f);
|
||||
p.actorValue.set("speed", 3.0f);
|
||||
p.actorValue.set("speedmult", 1.0f);
|
||||
p.actorValue.set("accel", p.WALK_ACCEL_BASE);
|
||||
p.actorValue.set("accelmult", 1.0f);
|
||||
|
||||
p.actorValue.set("jumppower", 6f);
|
||||
// in frames
|
||||
p.actorValue.set("jumplength", 30f);
|
||||
|
||||
p.actorValue.set("basemass", 80f);
|
||||
|
||||
p.actorValue.set("physiquemult", 1); // Constant 1.0 for player, meant to be used by random mobs
|
||||
/**
|
||||
* fixed value, or 'base value', from creature strength of Dwarf Fortress.
|
||||
* Human race uses 1000. (see CreatureHuman.json)
|
||||
*/
|
||||
p.actorValue.set("strength", 1250);
|
||||
p.actorValue.set("encumbrance", 1000);
|
||||
|
||||
p.setHitboxDimension(20, 47, 7, 0);
|
||||
|
||||
p.inventory = new ActorInventory((int) p.actorValue.get("encumbrance"), true);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
||||
449
src/com/Torvald/Terrarum/Actors/Player.java
Normal file
449
src/com/Torvald/Terrarum/Actors/Player.java
Normal file
@@ -0,0 +1,449 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.Terrarum.GameControl.EnumKeyFunc;
|
||||
import com.Torvald.Terrarum.GameControl.KeyMap;
|
||||
import com.Torvald.spriteAnimation.SpriteAnimation;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import org.newdawn.slick.*;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public class Player extends ActorWithBody implements Controllable, Pocketed {
|
||||
|
||||
@Nullable public Controllable vehicleRiding;
|
||||
|
||||
ActorValue actorValue;
|
||||
|
||||
int jumpPowerCounter = 0;
|
||||
int walkPowerCounter = 0;
|
||||
private final int WALK_FRAMES_TO_MAX_ACCEL = 6;
|
||||
|
||||
public float readonly_totalX = 0;
|
||||
|
||||
boolean jumping = false;
|
||||
|
||||
@NotNull int walkHeading;
|
||||
|
||||
ActorInventory inventory;
|
||||
|
||||
private final int LEFT = 1;
|
||||
private final int RIGHT = 2;
|
||||
|
||||
private int prevHMoveKey = -1;
|
||||
private int prevVMoveKey = -1;
|
||||
private final int KEY_NULL = -1;
|
||||
|
||||
final float ACCEL_MULT_IN_FLIGHT = 0.45f;
|
||||
final float WALK_STOP_ACCEL = 0.2f;
|
||||
final float WALK_ACCEL_BASE = 0.2f;
|
||||
|
||||
private boolean noClip = false;
|
||||
|
||||
/**
|
||||
* Creates new Player instance with empty elements (sprites, actorvalue, etc.). <br />
|
||||
*
|
||||
* <strong>Use PlayerBuildFactory to build player!</strong>
|
||||
*
|
||||
* @throws SlickException
|
||||
*/
|
||||
public Player() throws SlickException {
|
||||
super();
|
||||
actorValue = new ActorValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer gc, int delta_t) {
|
||||
updatePhysicalInfos();
|
||||
super.update(gc, delta_t);
|
||||
|
||||
updateSprite(delta_t);
|
||||
|
||||
updateMovementControl();
|
||||
|
||||
if (noClip) { super.setGrounded(true); }
|
||||
}
|
||||
|
||||
private void updatePhysicalInfos() {
|
||||
super.setScale((float) actorValue.get("scale"));
|
||||
super.setMass((float) actorValue.get("basemass")
|
||||
* FastMath.pow(super.getScale(), 3));
|
||||
}
|
||||
|
||||
private void walkHorizontal(boolean left) {
|
||||
readonly_totalX = super.getVeloX()
|
||||
+
|
||||
(float) actorValue.get("accel")
|
||||
* (float) actorValue.get("accelmult")
|
||||
* FastMath.sqrt(super.getScale())
|
||||
* applyAccelRealism(walkPowerCounter)
|
||||
* (left ? -1 : 1);
|
||||
|
||||
super.setVeloX(readonly_totalX);
|
||||
|
||||
if (walkPowerCounter < WALK_FRAMES_TO_MAX_ACCEL) {
|
||||
walkPowerCounter += 1;
|
||||
}
|
||||
|
||||
// Clamp veloX
|
||||
super.setVeloX(
|
||||
absClamp(super.getVeloX()
|
||||
, (float) actorValue.get("speed")
|
||||
* (float) actorValue.get("speedmult")
|
||||
* FastMath.sqrt(super.getScale())
|
||||
));
|
||||
|
||||
// Heading flag
|
||||
if (left)
|
||||
walkHeading = LEFT;
|
||||
else
|
||||
walkHeading = RIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* For realistic accelerating while walking.
|
||||
*
|
||||
* Naïve 'veloX += 3' is actually like:
|
||||
*
|
||||
* a
|
||||
* | ------------
|
||||
* |
|
||||
* |
|
||||
* 0+------············ t
|
||||
*
|
||||
* which is unrealistic, so this method tries to introduce some realism by:
|
||||
*
|
||||
* a
|
||||
* | ------------
|
||||
* | ---
|
||||
* | -
|
||||
* | ---
|
||||
* 0+----··················· t
|
||||
*
|
||||
*
|
||||
* @param x
|
||||
*/
|
||||
private float applyAccelRealism(int x) {
|
||||
return 0.5f + 0.5f * -FastMath.cos(10 * x / (WALK_FRAMES_TO_MAX_ACCEL * FastMath.PI));
|
||||
}
|
||||
|
||||
private void walkVertical(boolean up) {
|
||||
super.setVeloY(super.getVeloY()
|
||||
+
|
||||
(float) actorValue.get("accel")
|
||||
* (float) actorValue.get("accelmult")
|
||||
* FastMath.sqrt(super.getScale())
|
||||
* applyAccelRealism(walkPowerCounter)
|
||||
* (up ? -1 : 1)
|
||||
);
|
||||
|
||||
if (walkPowerCounter < WALK_FRAMES_TO_MAX_ACCEL) {
|
||||
walkPowerCounter += 1;
|
||||
}
|
||||
|
||||
// Clamp veloX
|
||||
super.setVeloY(
|
||||
absClamp(super.getVeloY()
|
||||
, (float) actorValue.get("speed")
|
||||
* (float) actorValue.get("speedmult")
|
||||
* FastMath.sqrt(super.getScale())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void walkHStop() {
|
||||
if (super.getVeloX() > 0) {
|
||||
super.setVeloX(super.getVeloX()
|
||||
-
|
||||
(float) actorValue.get("accel")
|
||||
* (float) actorValue.get("accelmult")
|
||||
* FastMath.sqrt(super.getScale())
|
||||
);
|
||||
|
||||
// compensate overshoot
|
||||
if (super.getVeloX() < 0)
|
||||
super.setVeloX(0);
|
||||
}
|
||||
else if (super.getVeloX() < 0) {
|
||||
super.setVeloX(super.getVeloX()
|
||||
+
|
||||
(float) actorValue.get("accel")
|
||||
* (float) actorValue.get("accelmult")
|
||||
* FastMath.sqrt(super.getScale())
|
||||
);
|
||||
|
||||
// compensate overshoot
|
||||
if (super.getVeloX() > 0)
|
||||
super.setVeloX(0);
|
||||
}
|
||||
else {
|
||||
super.setVeloX(0);
|
||||
}
|
||||
|
||||
walkPowerCounter = 0;
|
||||
}
|
||||
|
||||
private void walkVStop() {
|
||||
if (super.getVeloY() > 0) {
|
||||
super.setVeloY(super.getVeloY()
|
||||
-
|
||||
WALK_STOP_ACCEL
|
||||
* (float) actorValue.get("accelmult")
|
||||
* FastMath.sqrt(super.getScale())
|
||||
);
|
||||
|
||||
// compensate overshoot
|
||||
if (super.getVeloY() < 0)
|
||||
super.setVeloY(0);
|
||||
}
|
||||
else if (super.getVeloY() < 0) {
|
||||
super.setVeloY(super.getVeloY()
|
||||
+
|
||||
WALK_STOP_ACCEL
|
||||
* (float) actorValue.get("accelmult")
|
||||
* FastMath.sqrt(super.getScale())
|
||||
);
|
||||
|
||||
// compensate overshoot
|
||||
if (super.getVeloY() > 0)
|
||||
super.setVeloY(0);
|
||||
}
|
||||
else {
|
||||
super.setVeloY(0);
|
||||
}
|
||||
|
||||
walkPowerCounter = 0;
|
||||
}
|
||||
|
||||
private void updateMovementControl() {
|
||||
if (!noClip) {
|
||||
if (super.isGrounded()) {
|
||||
actorValue.set("accelmult", 1f);
|
||||
} else {
|
||||
actorValue.set("accelmult", ACCEL_MULT_IN_FLIGHT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
actorValue.set("accelmult", 1f);
|
||||
}
|
||||
}
|
||||
|
||||
public void processInput(Input input) {
|
||||
/**
|
||||
* L-R stop
|
||||
*/
|
||||
// ↑F, ↑S
|
||||
if (!isFuncDown(input, EnumKeyFunc.MOVE_LEFT)
|
||||
&& !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
walkHStop();
|
||||
prevHMoveKey = KEY_NULL;
|
||||
}
|
||||
/**
|
||||
* U-D stop
|
||||
*/
|
||||
// ↑E
|
||||
// ↑D
|
||||
if (isNoClip()
|
||||
&&!isFuncDown(input, EnumKeyFunc.MOVE_UP)
|
||||
&& !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
walkVStop();
|
||||
prevVMoveKey = KEY_NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left/Right movement
|
||||
*/
|
||||
|
||||
// ↑F, ↓S
|
||||
if (isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)
|
||||
&& !isFuncDown(input, EnumKeyFunc.MOVE_LEFT)) {
|
||||
walkHorizontal(false);
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT);
|
||||
}
|
||||
// ↓F, ↑S
|
||||
else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT)
|
||||
&& !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
walkHorizontal(true);
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT);
|
||||
}
|
||||
// ↓F, ↓S
|
||||
else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT)
|
||||
&& isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) {
|
||||
walkHorizontal(false);
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT);
|
||||
}
|
||||
else if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)) {
|
||||
walkHorizontal(true);
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Up/Down movement
|
||||
*/
|
||||
|
||||
if (noClip) {
|
||||
// ↑E
|
||||
// ↓D
|
||||
if (isFuncDown(input, EnumKeyFunc.MOVE_DOWN)
|
||||
&& !isFuncDown(input, EnumKeyFunc.MOVE_UP)) {
|
||||
walkVertical(false);
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN);
|
||||
}
|
||||
// ↓E
|
||||
// ↑D
|
||||
else if (isFuncDown(input, EnumKeyFunc.MOVE_UP)
|
||||
&& !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
walkVertical(true);
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP);
|
||||
}
|
||||
// ↓E
|
||||
// ↓D
|
||||
else if (isFuncDown(input, EnumKeyFunc.MOVE_UP)
|
||||
&& isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) {
|
||||
walkVertical(false);
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN);
|
||||
}
|
||||
else if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)) {
|
||||
walkVertical(true);
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump control
|
||||
*/
|
||||
if (isFuncDown(input, EnumKeyFunc.JUMP)) {
|
||||
if (!noClip) {
|
||||
if (super.isGrounded()) {
|
||||
jumping = true;
|
||||
jump();
|
||||
}
|
||||
}
|
||||
else {
|
||||
walkVertical(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
jumping = false;
|
||||
jumpPowerCounter = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void keyPressed(int key, char c) {
|
||||
|
||||
}
|
||||
|
||||
private void jump() {
|
||||
float len = (float) actorValue.get("jumplength");
|
||||
float pwr = (float) actorValue.get("jumppower");
|
||||
|
||||
//if (jumping) {
|
||||
// // Limit increment of jumpPowerCounter
|
||||
// if (jumpPowerCounter < len) {
|
||||
// jumpPowerCounter += 1;
|
||||
//
|
||||
// /**
|
||||
// * Limit jumping
|
||||
// */
|
||||
// //super.veloY = jumpFuncSqu(pwr, len);
|
||||
// super.veloY += jumpFuncLin(pwr, len);
|
||||
// //super.veloY = jumpFuncExp(pwr, len);
|
||||
//
|
||||
// System.out.println(jumpFuncLin(pwr, len));
|
||||
// }
|
||||
//
|
||||
// super.grounded = false;
|
||||
//}
|
||||
|
||||
// At least this works, though. Useful if it were AI-controlled.
|
||||
super.setVeloY(super.getVeloY()
|
||||
-
|
||||
pwr * FastMath.sqrt(super.getScale())
|
||||
);
|
||||
}
|
||||
|
||||
private float jumpFuncLin(float pwr, float len) {
|
||||
return -(pwr / len) * jumpPowerCounter;
|
||||
}
|
||||
|
||||
private float jumpFuncSqu(float pwr, float len) {
|
||||
return (pwr / (len * len))
|
||||
* (jumpPowerCounter - len)
|
||||
* (jumpPowerCounter - len) // square
|
||||
- pwr;
|
||||
}
|
||||
|
||||
private float jumpFuncExp(float pwr, float len) {
|
||||
float a = FastMath.pow(pwr + 1, 1 / len);
|
||||
return -FastMath.pow(a, len) + 1;
|
||||
}
|
||||
|
||||
private boolean isFuncDown(Input input, EnumKeyFunc fn) {
|
||||
return input.isKeyDown(KeyMap.getKeyCode(fn));
|
||||
}
|
||||
|
||||
private float absClamp(float i, float ceil) {
|
||||
if (i > 0)
|
||||
return (i > ceil) ? ceil : i;
|
||||
else if (i < 0)
|
||||
return (-i > ceil) ? -ceil : i;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void updateSprite(int delta_t) {
|
||||
sprite.update(delta_t);
|
||||
if (spriteGlow != null) {
|
||||
spriteGlow.update(delta_t);
|
||||
}
|
||||
|
||||
if (super.isGrounded()) {
|
||||
if (walkHeading == LEFT) {
|
||||
sprite.flip(true, false);
|
||||
if (spriteGlow != null) {
|
||||
spriteGlow.flip(true, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sprite.flip(false, false);
|
||||
if (spriteGlow != null) {
|
||||
spriteGlow.flip(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActorInventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void overwriteInventory(ActorInventory inventory) {
|
||||
this.inventory = inventory;
|
||||
}
|
||||
|
||||
public boolean isNoClip() {
|
||||
return noClip;
|
||||
}
|
||||
|
||||
public void setNoClip(boolean b) {
|
||||
noClip = b;
|
||||
}
|
||||
|
||||
public ActorValue getActorValue() {
|
||||
return actorValue;
|
||||
}
|
||||
|
||||
public SpriteAnimation getSpriteGlow() {
|
||||
return spriteGlow;
|
||||
}
|
||||
|
||||
}
|
||||
120
src/com/Torvald/Terrarum/Actors/PlayerBuildFactory.java
Normal file
120
src/com/Torvald/Terrarum/Actors/PlayerBuildFactory.java
Normal file
@@ -0,0 +1,120 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
|
||||
import com.Torvald.Rand.Fudge3;
|
||||
import com.Torvald.Rand.HighQualityRandom;
|
||||
import com.google.gson.*;
|
||||
import org.newdawn.slick.SlickException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-03.
|
||||
*/
|
||||
public class PlayerBuildFactory {
|
||||
|
||||
private static final String JSONPATH = "./res/raw/";
|
||||
private static String jsonString = new String();
|
||||
|
||||
public Player build(String jsonFileName) throws IOException, SlickException {
|
||||
JsonObject jsonObj = readJson(jsonFileName);
|
||||
Player p = new Player();
|
||||
|
||||
|
||||
String[] elementsString = {
|
||||
"racename"
|
||||
, "racenameplural"
|
||||
};
|
||||
|
||||
String[] elementsFloat = {
|
||||
"baseheight"
|
||||
, "basemass"
|
||||
, "toolsize"
|
||||
, "encumbrance"
|
||||
};
|
||||
|
||||
String[] elementsFloatVariable = {
|
||||
"baseheight"
|
||||
, "strength"
|
||||
, "speed"
|
||||
, "jumppower"
|
||||
, "scale"
|
||||
};
|
||||
|
||||
|
||||
setAVStrings(p, elementsString, jsonObj);
|
||||
setAVFloats(p, elementsFloat, jsonObj);
|
||||
setAVFloatsVariable(p, elementsFloatVariable, jsonObj);
|
||||
|
||||
|
||||
p.inventory = new ActorInventory((int) p.actorValue.get("encumberance"), true);
|
||||
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and set actor values that have 'variable' appended. E.g. strength
|
||||
* @param p
|
||||
* @param elemSet
|
||||
* @param jsonObject
|
||||
*/
|
||||
private void setAVFloatsVariable(Player p, String[] elemSet, JsonObject jsonObject) {
|
||||
for (String s : elemSet) {
|
||||
float baseValue = jsonObject.get(s).getAsFloat();
|
||||
// roll fudge dice and get value [-3, 3] as [0, 6]
|
||||
int varSelected = new Fudge3().create(new HighQualityRandom()).roll() + 3;
|
||||
// get multiplier from json. Assuming percentile
|
||||
int multiplier = jsonObject.get(s + "variable").getAsJsonArray().get(varSelected).getAsInt();
|
||||
float realValue = baseValue * multiplier / 100f;
|
||||
|
||||
p.actorValue.set(s, realValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and set string actor values
|
||||
* @param p
|
||||
* @param elemSet
|
||||
* @param jsonObject
|
||||
*/
|
||||
private void setAVStrings(Player p, String[] elemSet, JsonObject jsonObject) {
|
||||
for (String s : elemSet) {
|
||||
p.actorValue.set(s, jsonObject.get(s).getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and set float actor values
|
||||
* @param p
|
||||
* @param elemSet
|
||||
* @param jsonObject
|
||||
*/
|
||||
private void setAVFloats(Player p, String[] elemSet, JsonObject jsonObject) {
|
||||
for (String s : elemSet) {
|
||||
p.actorValue.set(s, jsonObject.get(s).getAsFloat());
|
||||
}
|
||||
}
|
||||
|
||||
private JsonObject readJson(String jsonFileName) throws IOException {
|
||||
readJsonFileAsString(jsonFileName);
|
||||
|
||||
JsonParser jsonParser = new JsonParser();
|
||||
JsonObject jsonObj = jsonParser.parse(jsonString).getAsJsonObject();
|
||||
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
private void readJsonFileAsString(String filename) throws IOException {
|
||||
Files.lines(
|
||||
FileSystems.getDefault().getPath(JSONPATH + filename)
|
||||
).forEach(this::strAppend);
|
||||
}
|
||||
|
||||
private void strAppend( String s) {
|
||||
jsonString += s;
|
||||
}
|
||||
|
||||
}
|
||||
45
src/com/Torvald/Terrarum/Actors/PlayerDebugger.java
Normal file
45
src/com/Torvald/Terrarum/Actors/PlayerDebugger.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.spriteAnimation.SpriteAnimation;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-14.
|
||||
*/
|
||||
public class PlayerDebugger {
|
||||
|
||||
private Actor actor;
|
||||
|
||||
public PlayerDebugger(Actor actor) {
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
if (actor instanceof Player) {
|
||||
return (Player) actor;
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Delegates for Player instances
|
||||
*/
|
||||
|
||||
public float baseHitboxW() { return getPlayer().getBaseHitboxW(); }
|
||||
public float baseHitboxH() { return getPlayer().getBaseHitboxH(); }
|
||||
public float hitboxTranslateX() { return getPlayer().getHitboxTranslateX(); }
|
||||
public float hitboxTranslateY() { return getPlayer().getHitboxTranslateY(); }
|
||||
public float veloX() { return getPlayer().getVeloX(); }
|
||||
public float veloY() { return getPlayer().getVeloY(); }
|
||||
public int baseSpriteWidth() { return getPlayer().baseSpriteWidth; }
|
||||
public int baseSpriteHeight() { return getPlayer().baseSpriteHeight; }
|
||||
public SpriteAnimation sprite() { return getPlayer().sprite; }
|
||||
public float scale() { return getPlayer().getScale(); }
|
||||
public Hitbox hitbox() { return getPlayer().getHitbox(); }
|
||||
public Hitbox nextHitbox() { return getPlayer().getNextHitbox(); }
|
||||
public boolean grounded() { return getPlayer().isGrounded(); }
|
||||
public ActorValue actorValue() { return getPlayer().getActorValue(); }
|
||||
public float mass() { return getPlayer().getMass(); }
|
||||
public boolean noClip() { return getPlayer().isNoClip(); }
|
||||
}
|
||||
12
src/com/Torvald/Terrarum/Actors/Pocketed.java
Normal file
12
src/com/Torvald/Terrarum/Actors/Pocketed.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
public interface Pocketed {
|
||||
|
||||
public ActorInventory getInventory();
|
||||
|
||||
public void overwriteInventory(ActorInventory inventory);
|
||||
|
||||
}
|
||||
91
src/com/Torvald/Terrarum/Actors/TestNPC.java
Normal file
91
src/com/Torvald/Terrarum/Actors/TestNPC.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import com.Torvald.Terrarum.GameItem.InventoryItem;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-31.
|
||||
*/
|
||||
public class TestNPC extends ActorWithBody implements AIControlled, Pocketed, CanBeStoredAsItem {
|
||||
|
||||
private InventoryItem itemData;
|
||||
// private ActorAI ai;
|
||||
private ActorInventory inventory;
|
||||
|
||||
@Override
|
||||
public void attachAI() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachItemData() {
|
||||
itemData = new InventoryItem() {
|
||||
@Override
|
||||
public float getWeight() {
|
||||
return getMass();
|
||||
}
|
||||
|
||||
/** Set no effect */
|
||||
@Override
|
||||
public void effectWhileInPocket(GameContainer gc, int delta_t) {
|
||||
|
||||
}
|
||||
|
||||
/** Set no effect */
|
||||
@Override
|
||||
public void effectWhenPickedUp(GameContainer gc, int delta_t) {
|
||||
|
||||
}
|
||||
|
||||
/** Set no effect */
|
||||
@Override
|
||||
public void primaryUse(GameContainer gc, int delta_t) {
|
||||
|
||||
}
|
||||
|
||||
/** Set no effect */
|
||||
@Override
|
||||
public void secondaryUse(GameContainer gc, int delta_t) {
|
||||
|
||||
}
|
||||
|
||||
/** Set no effect */
|
||||
@Override
|
||||
public void effectWhenThrownAway(GameContainer gc, int delta_t) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getItemWeight() {
|
||||
return super.getMass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopUpdateAndDraw() {
|
||||
super.setUpdate(false);
|
||||
super.setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeUpdateAndDraw() {
|
||||
super.setUpdate(true);
|
||||
super.setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryItem getItemData() {
|
||||
return itemData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActorInventory getInventory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void overwriteInventory(ActorInventory inventory) {
|
||||
this.inventory = inventory;
|
||||
}
|
||||
}
|
||||
15
src/com/Torvald/Terrarum/Actors/Visible.java
Normal file
15
src/com/Torvald/Terrarum/Actors/Visible.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.Torvald.Terrarum.Actors;
|
||||
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
public interface Visible {
|
||||
|
||||
void drawBody(GameContainer gc, Graphics g);
|
||||
|
||||
void updateBodySprite(GameContainer gc, int delta_t);
|
||||
|
||||
}
|
||||
6
src/com/Torvald/Terrarum/COPYING
Normal file
6
src/com/Torvald/Terrarum/COPYING
Normal file
@@ -0,0 +1,6 @@
|
||||
* Terrarum by Torvald
|
||||
Copyright 2015-2016 Torvald. All rights reserved.
|
||||
mailto: alswo9628 *at* !gmail! *dot* !com!
|
||||
|
||||
* Simplex Noise Generator, version 2012-03-09 by Stefan Gustavson
|
||||
Released as public domain
|
||||
45
src/com/Torvald/Terrarum/ConsoleCommand/CodexEdictis.java
Normal file
45
src/com/Torvald/Terrarum/ConsoleCommand/CodexEdictis.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.UserInterface.ConsoleWindow;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-16.
|
||||
*/
|
||||
public class CodexEdictis implements ConsoleCommand {
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
if (args.length == 1) {
|
||||
printList();
|
||||
}
|
||||
else{
|
||||
try {
|
||||
ConsoleCommand commandObj = CommandDict.getCommand(args[1].toLowerCase());
|
||||
commandObj.printUsage();
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
new Echo().execute("Codex: Unknown command: " + args[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
Echo echo = new Echo();
|
||||
echo.execute("Usage: codex (command)");
|
||||
echo.execute("shows how to use 'command'");
|
||||
echo.execute("leave blank to get list of available commands");
|
||||
}
|
||||
|
||||
private void printList() {
|
||||
Echo echo = new Echo();
|
||||
echo.execute("Available commands");
|
||||
echo.execute("--------------------------------");
|
||||
|
||||
CommandDict.dict.keySet().forEach((s) -> echo.execute(s));
|
||||
|
||||
|
||||
echo.execute("--------------------------------");
|
||||
}
|
||||
|
||||
}
|
||||
34
src/com/Torvald/Terrarum/ConsoleCommand/CommandDict.java
Normal file
34
src/com/Torvald/Terrarum/ConsoleCommand/CommandDict.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
public class CommandDict {
|
||||
|
||||
protected static Hashtable<String, ConsoleCommand> dict;
|
||||
|
||||
public CommandDict() {
|
||||
dict = new Hashtable<>();
|
||||
|
||||
dict.put("setav", new SetAV());
|
||||
dict.put("qqq", new QuitApp());
|
||||
dict.put("codex", new CodexEdictis());
|
||||
dict.put("export", new ExportMap());
|
||||
dict.put("gc", new ForceGC());
|
||||
dict.put("getav", new GetAV());
|
||||
dict.put("getlocale", new GetLocale());
|
||||
dict.put("togglenoclip", new ToggleNoClip());
|
||||
dict.put("nc", dict.get("togglenoclip"));
|
||||
dict.put("bulletintest", new SetBulletin());
|
||||
dict.put("setlocale", new SetLocale());
|
||||
dict.put("zoom", new Zoom());
|
||||
dict.put("teleport", new TeleportPlayer());
|
||||
}
|
||||
|
||||
public static ConsoleCommand getCommand(String commandName) {
|
||||
return dict.get(commandName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.UserInterface.ConsoleWindow;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
public class CommandInterpreter {
|
||||
|
||||
public static void execute(String command) {
|
||||
CommandInput[] cmd = parse(command);
|
||||
|
||||
for (CommandInput single_command : cmd) {
|
||||
try {
|
||||
ConsoleCommand commandObj = CommandDict.getCommand(single_command.getName().toLowerCase());
|
||||
commandObj.execute(single_command.toStringArray());
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
new Echo().execute("Unknown command: " + single_command.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static CommandInput[] parse(String input) {
|
||||
String[] commands = input.split(";[ ]?"); // split multiple commands (e.g. respawn player; setav player speed 5)
|
||||
|
||||
CommandInput[] ret = new CommandInput[commands.length];
|
||||
for (int i = 0; i < commands.length; i++) {
|
||||
ret[i] = new CommandInput(commands[i].split(" "));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CommandInput {
|
||||
private String[] args;
|
||||
private int argsCount = 0;
|
||||
|
||||
CommandInput(String[] s) {
|
||||
args = s;
|
||||
}
|
||||
|
||||
String[] toStringArray() {
|
||||
return args;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return args[0];
|
||||
}
|
||||
|
||||
int getArgsCount() {
|
||||
return argsCount;
|
||||
}
|
||||
}
|
||||
12
src/com/Torvald/Terrarum/ConsoleCommand/ConsoleCommand.java
Normal file
12
src/com/Torvald/Terrarum/ConsoleCommand/ConsoleCommand.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
interface ConsoleCommand {
|
||||
|
||||
void execute(String[] args);
|
||||
|
||||
void printUsage();
|
||||
|
||||
}
|
||||
25
src/com/Torvald/Terrarum/ConsoleCommand/Echo.java
Normal file
25
src/com/Torvald/Terrarum/ConsoleCommand/Echo.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.UserInterface.ConsoleWindow;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-16.
|
||||
*/
|
||||
class Echo implements ConsoleCommand {
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
((ConsoleWindow) Game.consoleHandler.getUI())
|
||||
.sendMessage(args.toString());
|
||||
}
|
||||
|
||||
public void execute(String single_line) {
|
||||
((ConsoleWindow) Game.consoleHandler.getUI())
|
||||
.sendMessage(single_line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
|
||||
}
|
||||
}
|
||||
148
src/com/Torvald/Terrarum/ConsoleCommand/ExportMap.java
Normal file
148
src/com/Torvald/Terrarum/ConsoleCommand/ExportMap.java
Normal file
@@ -0,0 +1,148 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.ColourUtil.Col12;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import org.newdawn.slick.Color;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-17.
|
||||
*/
|
||||
public class ExportMap implements ConsoleCommand {
|
||||
|
||||
private byte[] mapData;
|
||||
private int mapDataPointer = 0;
|
||||
|
||||
private static final byte AIR = 0;
|
||||
private static final byte COAL = 16;
|
||||
private static final byte COPPER = 17;
|
||||
private static final byte IRON = 18;
|
||||
private static final byte GOLD = 19;
|
||||
|
||||
private static final byte COBALTITE = 20;
|
||||
private static final byte ILMENITE = 21;
|
||||
private static final byte AURICHALCUM = 22;
|
||||
|
||||
private static final byte DIAMOND = 23;
|
||||
private static final byte RUBY = 24;
|
||||
private static final byte EMERALD = 25;
|
||||
private static final byte SAPPHIRE = 26;
|
||||
private static final byte TOPAZ = 27;
|
||||
private static final byte AMETHYST = 28;
|
||||
|
||||
private static final byte DIRT = 2;
|
||||
private static final byte GRAVEL = 15;
|
||||
private static final byte SAND = 14;
|
||||
private static final byte STONE = 1;
|
||||
private static final byte GRASS = 3;
|
||||
|
||||
private static final byte WATER = (byte) 239;
|
||||
private static final byte LAVA = (byte) 255;
|
||||
|
||||
private Hashtable<Byte, Col12> colorTable = new Hashtable<>();
|
||||
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
if (args.length == 2) {
|
||||
buildColorTable();
|
||||
|
||||
mapData = new byte[Game.map.width * Game.map.height * 3];
|
||||
|
||||
for (byte tile : Game.map.getLayerTerrain()) {
|
||||
byte[] colArray = colorTable.getOrDefault(tile, new Col12(0xFFF))
|
||||
.toByteArray();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
mapData[mapDataPointer + i] = colArray[i];
|
||||
}
|
||||
|
||||
mapDataPointer += 3;
|
||||
}
|
||||
|
||||
String dir = Terrarum.defaultDir + "/Exports/";
|
||||
File dirAsFile = new File(dir);
|
||||
if (!dirAsFile.exists()) {
|
||||
dirAsFile.mkdir();
|
||||
}
|
||||
|
||||
try {
|
||||
int[] bandOffsets = {0, 1, 2}; // RGB
|
||||
DataBuffer buffer = new DataBufferByte(mapData, mapData.length);
|
||||
WritableRaster raster = Raster.createInterleavedRaster(
|
||||
buffer
|
||||
, Game.map.width
|
||||
, Game.map.height
|
||||
, 3 * Game.map.width
|
||||
, 3
|
||||
, bandOffsets
|
||||
, null);
|
||||
|
||||
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
|
||||
|
||||
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
|
||||
|
||||
ImageIO.write(image, "PNG", new File(dir + args[1] + ".png"));
|
||||
|
||||
} catch (IOException e) {
|
||||
new Echo().execute("ExportMap: IOException raised.");
|
||||
}
|
||||
|
||||
mapData = null;
|
||||
mapDataPointer = 0;
|
||||
|
||||
// Free up some memory
|
||||
System.gc();
|
||||
|
||||
new Echo().execute("ExportMap: exported to " + args[1] + ".png");
|
||||
}
|
||||
else{
|
||||
printUsage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
Echo echo = new Echo();
|
||||
echo.execute("Usage: export <name>");
|
||||
echo.execute("Exports current map into visible image.");
|
||||
echo.execute("The image can be found at %adddata%/Terrarum/Exports");
|
||||
}
|
||||
|
||||
private void buildColorTable() {
|
||||
colorTable.put(AIR, new Col12(0xCEF));
|
||||
colorTable.put(STONE, new Col12(0x887));
|
||||
colorTable.put(DIRT, new Col12(0x763));
|
||||
colorTable.put(GRASS, new Col12(0x251));
|
||||
|
||||
colorTable.put(COAL, new Col12(0x221));
|
||||
colorTable.put(COPPER, new Col12(0x6A8));
|
||||
colorTable.put(IRON, new Col12(0xC75));
|
||||
colorTable.put(GOLD, new Col12(0xCB6));
|
||||
colorTable.put(COBALTITE, new Col12(0xDCD));
|
||||
colorTable.put(ILMENITE, new Col12(0x8AB));
|
||||
colorTable.put(AURICHALCUM, new Col12(0xD92));
|
||||
|
||||
colorTable.put(DIAMOND, new Col12(0x9CE));
|
||||
colorTable.put(RUBY, new Col12(0xB10));
|
||||
colorTable.put(EMERALD, new Col12(0x0B1));
|
||||
colorTable.put(SAPPHIRE, new Col12(0x01B));
|
||||
colorTable.put(TOPAZ, new Col12(0xC70));
|
||||
colorTable.put(AMETHYST, new Col12(0x70C));
|
||||
|
||||
colorTable.put(WATER, new Col12(0x038));
|
||||
colorTable.put(LAVA, new Col12(0xF50));
|
||||
|
||||
colorTable.put(SAND, new Col12(0xDCA));
|
||||
colorTable.put(GRAVEL, new Col12(0x664));
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
17
src/com/Torvald/Terrarum/ConsoleCommand/ForceGC.java
Normal file
17
src/com/Torvald/Terrarum/ConsoleCommand/ForceGC.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-18.
|
||||
*/
|
||||
public class ForceGC implements ConsoleCommand {
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
System.gc();
|
||||
new Echo().execute("Invoked System.gc");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
new Echo().execute("Invoke garbage collection of JVM.");
|
||||
}
|
||||
}
|
||||
47
src/com/Torvald/Terrarum/ConsoleCommand/GetAV.java
Normal file
47
src/com/Torvald/Terrarum/ConsoleCommand/GetAV.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.Actors.ActorValue;
|
||||
import com.Torvald.Terrarum.Game;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-19.
|
||||
*/
|
||||
public class GetAV implements ConsoleCommand {
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
Echo echo = new Echo();
|
||||
|
||||
if (args.length == 1) {
|
||||
// print all actorvalue of player
|
||||
ActorValue av = Game.getPlayer().getActorValue();
|
||||
Set keyset = av.getKeySet();
|
||||
|
||||
keyset.forEach(
|
||||
elem -> echo.execute(elem + " = " + av.get((String)elem))
|
||||
);
|
||||
|
||||
}
|
||||
else if (args.length != 3 && args.length != 2) {
|
||||
printUsage();
|
||||
}
|
||||
else if (args.length == 2) {
|
||||
echo.execute("player." + args[1] + ": "
|
||||
+ Game.getPlayer().getActorValue().get(args[1])
|
||||
);
|
||||
}
|
||||
else if (args.length == 3) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
Echo echo = new Echo();
|
||||
echo.execute("Get desired actor value of specific target.");
|
||||
echo.execute("Usage: getav (id) <av>");
|
||||
echo.execute("blank ID for player");
|
||||
}
|
||||
}
|
||||
27
src/com/Torvald/Terrarum/ConsoleCommand/GetLocale.java
Normal file
27
src/com/Torvald/Terrarum/ConsoleCommand/GetLocale.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.LangPack.Lang;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-22.
|
||||
*/
|
||||
public class GetLocale implements ConsoleCommand {
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
new Echo().execute(
|
||||
"Locale: "
|
||||
+ Lang.get("MENU_LANGUAGE_THIS")
|
||||
+ " ("
|
||||
+ Lang.get("MENU_LANGUAGE_THIS_EN")
|
||||
+ ")"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
Echo echo = new Echo();
|
||||
echo.execute("Usage: getlocale");
|
||||
echo.execute("Get name of locale currently using.");
|
||||
}
|
||||
}
|
||||
17
src/com/Torvald/Terrarum/ConsoleCommand/QuitApp.java
Normal file
17
src/com/Torvald/Terrarum/ConsoleCommand/QuitApp.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
public class QuitApp implements ConsoleCommand {
|
||||
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
|
||||
}
|
||||
}
|
||||
44
src/com/Torvald/Terrarum/ConsoleCommand/SetAV.java
Normal file
44
src/com/Torvald/Terrarum/ConsoleCommand/SetAV.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
class SetAV implements ConsoleCommand {
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
Echo echo = new Echo();
|
||||
echo.execute("Set actor value of specific target to desired value.");
|
||||
echo.execute("Usage: setav (id) <av> <val>");
|
||||
echo.execute("blank ID for player");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
Echo echo = new Echo();
|
||||
|
||||
// setav <id or "player"> <av> <val>
|
||||
if (args.length != 4 && args.length != 3) {
|
||||
printUsage();
|
||||
}
|
||||
else if (args.length == 3) {
|
||||
float val;
|
||||
try {
|
||||
val = new Float(args[2]);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
new Echo().execute("Wrong number input.");
|
||||
return;
|
||||
}
|
||||
|
||||
Game.getPlayer().getActorValue().set(args[1], val);
|
||||
echo.execute("Set " + args[1] + " to " + val);
|
||||
}
|
||||
else if (args.length == 4) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
34
src/com/Torvald/Terrarum/ConsoleCommand/SetBulletin.java
Normal file
34
src/com/Torvald/Terrarum/ConsoleCommand/SetBulletin.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.LangPack.Lang;
|
||||
import com.Torvald.Terrarum.UserInterface.Bulletin;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-23.
|
||||
*/
|
||||
public class SetBulletin implements ConsoleCommand {
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
new Echo().execute(Lang.get("APP_CALIBRATE_YOUR_MONITOR"));
|
||||
|
||||
String[] testMsg = {
|
||||
"SetBulletin: this is a test!"
|
||||
, "게임 내 방송입니다."
|
||||
};
|
||||
send(testMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually send bulletin
|
||||
* @param message real message
|
||||
*/
|
||||
public void send(String[] message) {
|
||||
((Bulletin) (Game.bulletin.getUI())).sendBulletin(message);
|
||||
}
|
||||
}
|
||||
38
src/com/Torvald/Terrarum/ConsoleCommand/SetLocale.java
Normal file
38
src/com/Torvald/Terrarum/ConsoleCommand/SetLocale.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.LangPack.Lang;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
public class SetLocale implements ConsoleCommand {
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
if (args.length == 2) {
|
||||
String prevLocale = Terrarum.gameLocale;
|
||||
Terrarum.gameLocale = args[1].toLowerCase();
|
||||
try {
|
||||
new Lang();
|
||||
new Echo().execute("Set locale to '" + Terrarum.gameLocale + "'.");
|
||||
}
|
||||
catch (IOException e) {
|
||||
new Echo().execute("Locale '"
|
||||
+ args[1].toLowerCase()
|
||||
+ "' does not exist or could not read file."
|
||||
);
|
||||
Terrarum.gameLocale = prevLocale;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printUsage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
new Echo().execute("Usage: setlocale [locale]");
|
||||
}
|
||||
}
|
||||
36
src/com/Torvald/Terrarum/ConsoleCommand/TeleportPlayer.java
Normal file
36
src/com/Torvald/Terrarum/ConsoleCommand/TeleportPlayer.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.MapDrawer.MapDrawer;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-24.
|
||||
*/
|
||||
public class TeleportPlayer implements ConsoleCommand {
|
||||
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
if (args.length != 3) {
|
||||
printUsage();
|
||||
}
|
||||
else {
|
||||
|
||||
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);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
new Echo().execute("Wrong number input.");
|
||||
return;
|
||||
}
|
||||
|
||||
Game.getPlayer().setPosition(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
new Echo().execute("Usage: teleport [x-tile] [y-tile]");
|
||||
}
|
||||
}
|
||||
21
src/com/Torvald/Terrarum/ConsoleCommand/ToggleNoClip.java
Normal file
21
src/com/Torvald/Terrarum/ConsoleCommand/ToggleNoClip.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-19.
|
||||
*/
|
||||
public class ToggleNoClip implements ConsoleCommand {
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
boolean status = Game.getPlayer().isNoClip();
|
||||
|
||||
Game.getPlayer().setNoClip(!status);
|
||||
new Echo().execute("Set no-clip status to " + String.valueOf(!status));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
new Echo().execute("toggle no-clip status of player");
|
||||
}
|
||||
}
|
||||
44
src/com/Torvald/Terrarum/ConsoleCommand/Zoom.java
Normal file
44
src/com/Torvald/Terrarum/ConsoleCommand/Zoom.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
public class Zoom implements ConsoleCommand {
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
if (args.length == 2) {
|
||||
|
||||
float zoom;
|
||||
try {
|
||||
zoom = new Float(args[1]);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
new Echo().execute("Wrong number input.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (zoom < Game.ZOOM_MIN) {
|
||||
zoom = Game.ZOOM_MIN;
|
||||
}
|
||||
else if (zoom > Game.ZOOM_MAX) {
|
||||
zoom = Game.ZOOM_MAX;
|
||||
}
|
||||
|
||||
Game.screenZoom = zoom;
|
||||
|
||||
System.gc();
|
||||
|
||||
new Echo().execute("Set screen zoom to " + String.valueOf(zoom));
|
||||
}
|
||||
else {
|
||||
printUsage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
new Echo().execute("Usage: zoom [zoom]");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.Torvald.Terrarum.Exceptions;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-04.
|
||||
*/
|
||||
public class InvalidValueException extends Exception {
|
||||
|
||||
}
|
||||
211
src/com/Torvald/Terrarum/Game.java
Normal file
211
src/com/Torvald/Terrarum/Game.java
Normal file
@@ -0,0 +1,211 @@
|
||||
package com.Torvald.Terrarum;
|
||||
|
||||
import com.Torvald.Terrarum.Actors.*;
|
||||
import com.Torvald.Terrarum.ConsoleCommand.CommandDict;
|
||||
import com.Torvald.Terrarum.GameControl.GameController;
|
||||
import com.Torvald.Terrarum.GameControl.KeyToggler;
|
||||
import com.Torvald.Terrarum.GameMap.GameMap;
|
||||
import com.Torvald.Terrarum.MapDrawer.LightmapRenderer;
|
||||
import com.Torvald.Terrarum.MapDrawer.MapCamera;
|
||||
import com.Torvald.Terrarum.MapDrawer.MapDrawer;
|
||||
import com.Torvald.Terrarum.MapGenerator.MapGenerator;
|
||||
import com.Torvald.Terrarum.TileStat.TileStat;
|
||||
import com.Torvald.Terrarum.UserInterface.*;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.newdawn.slick.*;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.fills.GradientFill;
|
||||
import org.newdawn.slick.geom.Rectangle;
|
||||
import shader.Shader;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-30.
|
||||
*/
|
||||
public class Game {
|
||||
|
||||
static int game_mode = 0;
|
||||
|
||||
public static GameConfig gameConfig;
|
||||
|
||||
public static GameMap map;
|
||||
|
||||
public static LinkedList<Actor> actorContainer = new LinkedList<>();
|
||||
public static LinkedList<UIHandler> uiContainer = new LinkedList<>();
|
||||
|
||||
public static UIHandler consoleHandler;
|
||||
public static UIHandler debugWindow;
|
||||
public static UIHandler bulletin;
|
||||
|
||||
@NotNull
|
||||
static Player player;
|
||||
|
||||
public static final long PLAYER_REF_ID = 0x51621D;
|
||||
|
||||
private static Image GRADIENT_IMAGE;
|
||||
private static Rectangle skyBox;
|
||||
|
||||
public static float screenZoom = 1.0f;
|
||||
public static final float ZOOM_MAX = 2.0f;
|
||||
public static final float ZOOM_MIN = 0.25f;
|
||||
|
||||
private static Shader shader12BitCol;
|
||||
private static Shader shaderBlurH;
|
||||
private static Shader shaderBlurV;
|
||||
|
||||
public Game() throws SlickException {
|
||||
gameConfig = new GameConfig();
|
||||
gameConfig.addKey("smoothlighting", true);
|
||||
|
||||
shader12BitCol = Shader.makeShader("./res/4096.vrt", "./res/4096.frg");
|
||||
shaderBlurH = Shader.makeShader("./res/blurH.vrt", "./res/blur.frg");
|
||||
shaderBlurV = Shader.makeShader("./res/blurV.vrt", "./res/blur.frg");
|
||||
|
||||
GRADIENT_IMAGE = new Image("res/graphics/backgroundGradientColour.png");
|
||||
skyBox = new Rectangle(0, 0, Terrarum.WIDTH, Terrarum.HEIGHT);
|
||||
|
||||
new WorldTime();
|
||||
|
||||
map = new GameMap(8192, 2048);
|
||||
map.setGravitation(9.8f);
|
||||
|
||||
MapGenerator.attachMap(map);
|
||||
MapGenerator.setSeed(0x51621D);
|
||||
MapGenerator.generateMap();
|
||||
|
||||
new CommandDict();
|
||||
|
||||
// add new player and put it to actorContainer
|
||||
//player = new Player();
|
||||
player = new PBFSigrid().build();
|
||||
player.setPosition(24, 24);
|
||||
//player.setNoClip(true);
|
||||
actorContainer.add(player);
|
||||
|
||||
new MapDrawer(map);
|
||||
|
||||
new LightmapRenderer();
|
||||
|
||||
consoleHandler = new UIHandler(new ConsoleWindow());
|
||||
consoleHandler.setPosition(0, 0);
|
||||
|
||||
debugWindow = new UIHandler(new BasicDebugInfoWindow());
|
||||
debugWindow.setPosition(0, 0);
|
||||
|
||||
/*bulletin = new UIHandler(new Bulletin());
|
||||
bulletin.setPosition(
|
||||
(Terrarum.WIDTH - bulletin.getUI().getWidth())
|
||||
/ 2
|
||||
, 0
|
||||
);
|
||||
bulletin.setVisibility(true);
|
||||
*/
|
||||
|
||||
UIHandler msgtest = new UIHandler(new Message(400, true));
|
||||
String[] msg = {"Hello, world!", "안녕, 세상아!"};
|
||||
((Message) msgtest.getUI()).setMessage(msg);
|
||||
msgtest.setPosition(32, 32);
|
||||
// msgtest.setVisibility(true);
|
||||
|
||||
uiContainer.add(msgtest);
|
||||
}
|
||||
|
||||
public static Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public static void update(GameContainer gc, int delta_t) {
|
||||
MapDrawer.update(gc, delta_t);
|
||||
|
||||
GameController.processInput(gc.getInput());
|
||||
|
||||
actorContainer.forEach(actor -> actor.update(gc, delta_t));
|
||||
actorContainer.forEach(
|
||||
actor -> {
|
||||
if (actor instanceof Visible) {
|
||||
((Visible) actor).updateBodySprite(gc, delta_t);
|
||||
}
|
||||
if (actor instanceof Glowing) {
|
||||
((Glowing) actor).updateGlowSprite(gc, delta_t);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
uiContainer.forEach(ui -> ui.update(gc, delta_t));
|
||||
|
||||
KeyToggler.update(gc);
|
||||
|
||||
//bulletin.update(gc, delta_t);
|
||||
|
||||
TileStat.update();
|
||||
}
|
||||
|
||||
public static void render(GameContainer gc, Graphics g) {
|
||||
// shader12BitCol.setUniformIntVariable("pixelSize", 1);
|
||||
// shader12BitCol.startShader();
|
||||
// shaderBlurH.startShader();
|
||||
// shaderBlurV.startShader();
|
||||
|
||||
drawSkybox(g);
|
||||
|
||||
// compensate for zoom. UIs have to be treated specially! (see UIHandler)
|
||||
g.translate(
|
||||
-MapCamera.getCameraX() * screenZoom
|
||||
, -MapCamera.getCameraY() * screenZoom
|
||||
);
|
||||
|
||||
MapDrawer.render(gc, g);
|
||||
actorContainer.forEach(
|
||||
actor -> {
|
||||
if (actor instanceof Visible) {
|
||||
((Visible) actor).drawBody(gc, g);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Slick's MODE_COLOR_MULTIPLY is clearly broken... using GL11
|
||||
LightmapRenderer.renderLightMap();
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
// draw lightmap
|
||||
LightmapRenderer.draw(g);
|
||||
// draw environment colour overlay
|
||||
// MapDrawer.drawEnvOverlay(g);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
g.setDrawMode(Graphics.MODE_NORMAL);
|
||||
|
||||
actorContainer.forEach(
|
||||
actor -> {
|
||||
if (actor instanceof Glowing) {
|
||||
((Glowing) actor).drawGlow(gc, g);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
uiContainer.forEach(ui -> ui.render(gc, g));
|
||||
debugWindow.render(gc, g);
|
||||
consoleHandler.render(gc, g);
|
||||
//bulletin.render(gc, g);
|
||||
}
|
||||
|
||||
private static Color[] getGradientColour(int timeSec) {
|
||||
Color[] colourTable = new Color[2];
|
||||
|
||||
int gradMapWidth = GRADIENT_IMAGE.getWidth();
|
||||
int phase = Math.round((timeSec / WorldTime.DAY_LENGTH) * gradMapWidth);
|
||||
|
||||
//update in every 60 frames
|
||||
colourTable[0] = GRADIENT_IMAGE.getColor(phase, 0);
|
||||
colourTable[1] = GRADIENT_IMAGE.getColor(phase, 1);
|
||||
|
||||
return colourTable;
|
||||
}
|
||||
|
||||
private static void drawSkybox(Graphics g) {
|
||||
Color[] colourTable = getGradientColour(WorldTime.elapsedSeconds());
|
||||
GradientFill skyColourFill = new GradientFill(0, 0, colourTable[0], 0, Terrarum.HEIGHT, colourTable[1]);
|
||||
g.fill(skyBox, skyColourFill);
|
||||
}
|
||||
}
|
||||
35
src/com/Torvald/Terrarum/GameConfig.java
Normal file
35
src/com/Torvald/Terrarum/GameConfig.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.Torvald.Terrarum;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-30.
|
||||
*/
|
||||
public class GameConfig {
|
||||
|
||||
private Hashtable<String, Object> configTable;
|
||||
|
||||
public GameConfig() {
|
||||
this.configTable = new Hashtable<String, Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add key-value pair to the configuration table.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void addKey(String key, Object value){
|
||||
configTable.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value using key from configuration table.
|
||||
*
|
||||
* @param key
|
||||
* @return Object value
|
||||
*/
|
||||
public Object get(String key){
|
||||
return configTable.get(key);
|
||||
}
|
||||
}
|
||||
9
src/com/Torvald/Terrarum/GameControl/EnumKeyFunc.java
Normal file
9
src/com/Torvald/Terrarum/GameControl/EnumKeyFunc.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.Torvald.Terrarum.GameControl;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public enum EnumKeyFunc{
|
||||
UI_CONSOLE, UI_BASIC_INFO,
|
||||
MOVE_LEFT, MOVE_RIGHT, MOVE_UP, MOVE_DOWN, JUMP
|
||||
}
|
||||
104
src/com/Torvald/Terrarum/GameControl/GameController.java
Normal file
104
src/com/Torvald/Terrarum/GameControl/GameController.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package com.Torvald.Terrarum.GameControl;
|
||||
|
||||
import com.Torvald.Terrarum.Actors.Controllable;
|
||||
import com.Torvald.Terrarum.Actors.Player;
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.UserInterface.UIHandler;
|
||||
import org.newdawn.slick.Input;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public class GameController {
|
||||
|
||||
private static KeyMap keyMap;
|
||||
|
||||
private static Player player;
|
||||
private static Controllable playerVehicle;
|
||||
|
||||
public GameController() {
|
||||
player = Game.getPlayer();
|
||||
}
|
||||
|
||||
public static void setKeyMap(KeyMap map) {
|
||||
keyMap = map;
|
||||
}
|
||||
|
||||
public static void processInput(Input input) {
|
||||
if (!Game.consoleHandler.isTakingControl()) {
|
||||
if (playerVehicle != null) {
|
||||
playerVehicle.processInput(input);
|
||||
}
|
||||
|
||||
player.processInput(input);
|
||||
|
||||
for (UIHandler ui : Game.uiContainer) {
|
||||
ui.processInput(input);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Game.consoleHandler.processInput(input);
|
||||
}
|
||||
}
|
||||
|
||||
public static void keyPressed(int key, char c) {
|
||||
if (keyPressedByCode(key, EnumKeyFunc.UI_CONSOLE)) {
|
||||
Game.consoleHandler.toggleOpening();
|
||||
}
|
||||
else if (keyPressedByCode(key, EnumKeyFunc.UI_BASIC_INFO)) {
|
||||
Game.debugWindow.toggleOpening();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!Game.consoleHandler.isTakingControl()) {
|
||||
if (playerVehicle != null) {
|
||||
playerVehicle.keyPressed(key, c);
|
||||
}
|
||||
|
||||
player.keyPressed(key, c);
|
||||
}
|
||||
else {
|
||||
Game.consoleHandler.keyPressed(key, c);
|
||||
}
|
||||
|
||||
//System.out.println(String.valueOf(key) + ", " + String.valueOf(c));
|
||||
}
|
||||
|
||||
public static void keyReleased(int key, char c) {
|
||||
|
||||
}
|
||||
|
||||
public static void mouseMoved(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
public static void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
public static void mousePressed(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
public static void mouseReleased(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
public static void mouseWheelMoved(int change) {
|
||||
|
||||
}
|
||||
|
||||
public static void controllerButtonPressed(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
public static void controllerButtonReleased(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
private static boolean keyPressedByCode(int key, EnumKeyFunc fn) {
|
||||
return (KeyMap.getKeyCode(fn) == key);
|
||||
}
|
||||
|
||||
}
|
||||
86
src/com/Torvald/Terrarum/GameControl/Key.java
Normal file
86
src/com/Torvald/Terrarum/GameControl/Key.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package com.Torvald.Terrarum.GameControl;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
public class Key {
|
||||
|
||||
public static final int RET = 28;
|
||||
public static final int BKSP = 14;
|
||||
public static final int GRAVE = 41;
|
||||
public static final int TAB = 15;
|
||||
public static final int ESCAPE = 1;
|
||||
public static final int SPACE = 57;
|
||||
|
||||
public static final int L_SHIFT = 42;
|
||||
public static final int R_SHIFT = 54;
|
||||
|
||||
public static final int UP = 200;
|
||||
public static final int DOWN = 208;
|
||||
public static final int LEFT = 203;
|
||||
public static final int RIGHT = 205;
|
||||
|
||||
public static final int F1 = 59;
|
||||
public static final int F2 = 60;
|
||||
public static final int F3 = 61;
|
||||
public static final int F4 = 62;
|
||||
|
||||
public static final int F5 = 63;
|
||||
public static final int F6 = 64;
|
||||
public static final int F7 = 65;
|
||||
public static final int F8 = 66;
|
||||
|
||||
public static final int F9 = 67;
|
||||
public static final int F10 = 68;
|
||||
public static final int F11 = 87;
|
||||
public static final int F12 = 88;
|
||||
|
||||
public static final int NUM_1 = 2;
|
||||
public static final int NUM_2 = 3;
|
||||
public static final int NUM_3 = 4;
|
||||
public static final int NUM_4 = 5;
|
||||
public static final int NUM_5 = 6;
|
||||
public static final int NUM_6 = 7;
|
||||
public static final int NUM_7 = 8;
|
||||
public static final int NUM_8 = 9;
|
||||
public static final int NUM_9 = 10;
|
||||
public static final int NUM_0 = 11;
|
||||
|
||||
public static final int Q = 16;
|
||||
public static final int W = 17;
|
||||
public static final int E = 18;
|
||||
public static final int R = 19;
|
||||
public static final int T = 20;
|
||||
|
||||
public static final int Y = 21;
|
||||
public static final int U = 22;
|
||||
public static final int I = 23;
|
||||
public static final int O = 24;
|
||||
public static final int P = 25;
|
||||
|
||||
public static final int A = 30;
|
||||
public static final int S = 31;
|
||||
public static final int D = 32;
|
||||
public static final int F = 33;
|
||||
public static final int G = 34;
|
||||
|
||||
public static final int H = 35;
|
||||
public static final int J = 36;
|
||||
public static final int K = 37;
|
||||
public static final int L = 38;
|
||||
public static final int SEMICOLON = 39;
|
||||
|
||||
public static final int Z = 44;
|
||||
public static final int X = 45;
|
||||
public static final int C = 46;
|
||||
public static final int V = 47;
|
||||
public static final int B = 48;
|
||||
|
||||
public static final int N = 49;
|
||||
public static final int M = 50;
|
||||
|
||||
public static final int PGUP = 201;
|
||||
public static final int PGDN = 209;
|
||||
public static final int HOME = 199;
|
||||
public static final int END = 207;
|
||||
}
|
||||
31
src/com/Torvald/Terrarum/GameControl/KeyMap.java
Normal file
31
src/com/Torvald/Terrarum/GameControl/KeyMap.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.Torvald.Terrarum.GameControl;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public class KeyMap {
|
||||
|
||||
public static Hashtable<EnumKeyFunc, Integer> map_code = new Hashtable<>();
|
||||
|
||||
public static void build(){
|
||||
|
||||
map_code.put(EnumKeyFunc.MOVE_UP, Key.E);
|
||||
map_code.put(EnumKeyFunc.MOVE_LEFT, Key.S);
|
||||
map_code.put(EnumKeyFunc.MOVE_DOWN, Key.D);
|
||||
map_code.put(EnumKeyFunc.MOVE_RIGHT, Key.F);
|
||||
map_code.put(EnumKeyFunc.JUMP, Key.SPACE);
|
||||
map_code.put(EnumKeyFunc.UI_CONSOLE, Key.GRAVE);
|
||||
map_code.put(EnumKeyFunc.UI_BASIC_INFO, Key.F3);
|
||||
}
|
||||
|
||||
public static int getKeyCode(EnumKeyFunc fn) {
|
||||
return map_code.get(fn);
|
||||
}
|
||||
|
||||
public static void set(EnumKeyFunc func, int key){
|
||||
map_code.put(func, key);
|
||||
}
|
||||
|
||||
}
|
||||
49
src/com/Torvald/Terrarum/GameControl/KeyToggler.java
Normal file
49
src/com/Torvald/Terrarum/GameControl/KeyToggler.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package com.Torvald.Terrarum.GameControl;
|
||||
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Input;
|
||||
|
||||
public class KeyToggler {
|
||||
|
||||
private static boolean[] currentState = new boolean[256];
|
||||
private static boolean[] isPressed = new boolean[256];
|
||||
private static boolean[] isToggled = new boolean[256];
|
||||
|
||||
public static boolean isOn(int key){
|
||||
return currentState[key];
|
||||
}
|
||||
|
||||
public static void update(GameContainer gc){
|
||||
|
||||
Input input = gc.getInput();
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (input.isKeyDown(i)) {
|
||||
isPressed[i] = true;
|
||||
} else {
|
||||
isPressed[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 256; i++){
|
||||
if (isPressed[i] && !currentState[i] && !isToggled[i]){
|
||||
currentState[i] = true;
|
||||
isToggled[i] = true;
|
||||
}
|
||||
else if(isPressed[i] && currentState[i] && !isToggled[i]){
|
||||
currentState[i] = false;
|
||||
isToggled[i] = true;
|
||||
}
|
||||
|
||||
if (!isPressed[i] && isToggled[i]){
|
||||
isToggled[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void forceSet(int key, boolean b) {
|
||||
currentState[key] = b;
|
||||
isToggled[key] = true;
|
||||
}
|
||||
|
||||
}
|
||||
51
src/com/Torvald/Terrarum/GameItem/InventoryItem.java
Normal file
51
src/com/Torvald/Terrarum/GameItem/InventoryItem.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package com.Torvald.Terrarum.GameItem;
|
||||
|
||||
import org.newdawn.slick.GameContainer;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-16.
|
||||
*/
|
||||
public interface InventoryItem {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
||||
130
src/com/Torvald/Terrarum/GameMap/GameMap.java
Normal file
130
src/com/Torvald/Terrarum/GameMap/GameMap.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* MapLoader version 1.2
|
||||
* Release date 2013-05-20
|
||||
* Copyright 2013 SKYHi14
|
||||
*
|
||||
* The program is distributed in GNU GPL Licence version 3.
|
||||
* See http://www.gnu.org/licenses/gpl.html for information.
|
||||
*/
|
||||
|
||||
package com.Torvald.Terrarum.GameMap;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import org.newdawn.slick.SlickException;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class GameMap {
|
||||
|
||||
//layers
|
||||
private MapLayer layerWall;
|
||||
private MapLayer layerTerrain;
|
||||
private MapLayer layerWire;
|
||||
|
||||
//properties
|
||||
public int width;
|
||||
public int height;
|
||||
public int spawnX;
|
||||
public int spawnY;
|
||||
int offset;
|
||||
|
||||
public LinkedList<MapPoint> houseDesignation;
|
||||
|
||||
//public World physWorld = new World( new Vec2(0, -TerrarumMain.game.gravitationalAccel) );
|
||||
//physics
|
||||
@NotNull
|
||||
private float gravitation;
|
||||
|
||||
/**
|
||||
* @param width
|
||||
* @param height
|
||||
* @throws SlickException
|
||||
*/
|
||||
public GameMap(int width, int height) throws SlickException {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.spawnX = width / 2;
|
||||
this.spawnY = 200;
|
||||
|
||||
layerTerrain = new MapLayer(width, height);
|
||||
layerWall = new MapLayer(width, height);
|
||||
layerWire = new MapLayer(width, height);
|
||||
}
|
||||
|
||||
public void setGravitation(float g) {
|
||||
gravitation = g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 2d array data of terrain
|
||||
*
|
||||
* @return byte[][] terrain layer
|
||||
*/
|
||||
public byte[][] getTerrainArray() {
|
||||
return layerTerrain.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 2d array data of wall
|
||||
*
|
||||
* @return byte[][] wall layer
|
||||
*/
|
||||
public byte[][] getWallArray() {
|
||||
return layerWall.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 2d array data of wire
|
||||
*
|
||||
* @return byte[][] wire layer
|
||||
*/
|
||||
public byte[][] getWireArray() {
|
||||
return layerWire.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MapLayer object of terrain
|
||||
*
|
||||
* @return MapLayer terrain layer
|
||||
*/
|
||||
public MapLayer getLayerTerrain() {
|
||||
return layerTerrain;
|
||||
}
|
||||
|
||||
public MapLayer getLayerWall() {
|
||||
return layerWall;
|
||||
}
|
||||
|
||||
public MapLayer getLayerWire() {
|
||||
return layerWire;
|
||||
}
|
||||
|
||||
public int getTileFromWall(int x, int y) {
|
||||
return uint8ToInt32(layerWall.data[y][x]);
|
||||
}
|
||||
|
||||
public int getTileFromTerrain(int x, int y) {
|
||||
return uint8ToInt32(layerTerrain.data[y][x]);
|
||||
}
|
||||
|
||||
public int getTileFromWire(int x, int y) {
|
||||
return uint8ToInt32(layerWire.data[y][x]);
|
||||
}
|
||||
|
||||
private int uint8ToInt32(byte x) {
|
||||
int ret;
|
||||
if ((x & 0b1000_0000) != 0) {
|
||||
ret = (x & 0b0111_1111) | (x & 0b1000_0000);
|
||||
} else {
|
||||
ret = x;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public float getGravitation() {
|
||||
return gravitation;
|
||||
}
|
||||
}
|
||||
124
src/com/Torvald/Terrarum/GameMap/MapLayer.java
Normal file
124
src/com/Torvald/Terrarum/GameMap/MapLayer.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package com.Torvald.Terrarum.GameMap;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-17.
|
||||
*/
|
||||
public class MapLayer implements Iterable<Byte> {
|
||||
|
||||
byte[][] data;
|
||||
|
||||
public int width;
|
||||
public int height;
|
||||
|
||||
public static final int TILES_SUPPORTED = 256;
|
||||
|
||||
public MapLayer(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
data = new byte[height][width];
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
data[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over elements of type {@code T}.
|
||||
*
|
||||
* @return an Iterator.
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Byte> iterator() {
|
||||
Iterator<Byte> it = new Iterator<Byte>() {
|
||||
|
||||
private int iteratorCount = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iteratorCount < width * height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte next() {
|
||||
int y = iteratorCount / width;
|
||||
int x = iteratorCount % width;
|
||||
// advance counter
|
||||
iteratorCount += 1;
|
||||
|
||||
return data[y][x];
|
||||
}
|
||||
};
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the given action for each element of the {@code Iterable}
|
||||
* until all elements have been processed or the action throws an
|
||||
* exception. Unless otherwise specified by the implementing class,
|
||||
* actions are performed in the order of iteration (if an iteration order
|
||||
* is specified). Exceptions thrown by the action are relayed to the
|
||||
* caller.
|
||||
*
|
||||
* @param action The action to be performed for each element
|
||||
* @throws NullPointerException if the specified action is null
|
||||
* @implSpec <p>The default implementation behaves as if:
|
||||
* <pre>{@code
|
||||
* for (T t : this)
|
||||
* action.accept(t);
|
||||
* }</pre>
|
||||
* @since 1.8
|
||||
*/
|
||||
@Override
|
||||
public void forEach(Consumer action) {
|
||||
for (Byte b : this) {
|
||||
action.accept(b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link java.util.Spliterator} over the elements described by this
|
||||
* {@code Iterable}.
|
||||
*
|
||||
* @return a {@code Spliterator} over the elements described by this
|
||||
* {@code Iterable}.
|
||||
* @implSpec The default implementation creates an
|
||||
* <em><a href="Spliterator.html#binding">early-binding</a></em>
|
||||
* spliterator from the iterable's {@code Iterator}. The spliterator
|
||||
* inherits the <em>fail-fast</em> properties of the iterable's iterator.
|
||||
* @implNote The default implementation should usually be overridden. The
|
||||
* spliterator returned by the default implementation has poor splitting
|
||||
* capabilities, is unsized, and does not report any spliterator
|
||||
* characteristics. Implementing classes can nearly always provide a
|
||||
* better implementation.
|
||||
* @since 1.8
|
||||
*/
|
||||
@Override
|
||||
public Spliterator spliterator() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public int getTile(int x, int y) {
|
||||
return uint8ToInt32(data[y][x]);
|
||||
}
|
||||
|
||||
|
||||
private int uint8ToInt32(byte x) {
|
||||
int ret;
|
||||
if ((x & 0b1000_0000) != 0) {
|
||||
ret = (x & 0b0111_1111) | (x & 0b1000_0000);
|
||||
}
|
||||
else {
|
||||
ret = x;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
39
src/com/Torvald/Terrarum/GameMap/MapPoint.java
Normal file
39
src/com/Torvald/Terrarum/GameMap/MapPoint.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.Torvald.Terrarum.GameMap;
|
||||
|
||||
import com.Torvald.Point.Point2f;
|
||||
|
||||
|
||||
public class MapPoint {
|
||||
private Point2f startPoint;
|
||||
private Point2f endPoint;
|
||||
|
||||
public MapPoint(){
|
||||
|
||||
}
|
||||
|
||||
public MapPoint(Point2f p1, Point2f p2){
|
||||
setPoint(p1, p2);
|
||||
}
|
||||
|
||||
public MapPoint(int x1, int y1, int x2, int y2){
|
||||
setPoint(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
public void setPoint(Point2f p1, Point2f p2){
|
||||
startPoint = p1;
|
||||
endPoint = p2;
|
||||
}
|
||||
|
||||
public void setPoint(int x1, int y1, int x2, int y2){
|
||||
startPoint = new Point2f(x1, y1);
|
||||
endPoint = new Point2f(x2, y2);
|
||||
}
|
||||
|
||||
public Point2f getStartPoint(){
|
||||
return startPoint;
|
||||
}
|
||||
|
||||
public Point2f getEndPoint(){
|
||||
return endPoint;
|
||||
}
|
||||
}
|
||||
26
src/com/Torvald/Terrarum/LangPack/Lang.java
Normal file
26
src/com/Torvald/Terrarum/LangPack/Lang.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.Torvald.Terrarum.LangPack;
|
||||
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-22.
|
||||
*/
|
||||
public class Lang {
|
||||
|
||||
private static Properties lang;
|
||||
|
||||
public Lang() throws IOException {
|
||||
lang = new Properties();
|
||||
lang.load(new BufferedReader(new InputStreamReader(new FileInputStream(
|
||||
"res/locales/" + Terrarum.gameLocale + ".lang"), StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
public static String get(String key) {
|
||||
return lang.getProperty(key, key);
|
||||
}
|
||||
|
||||
}
|
||||
28
src/com/Torvald/Terrarum/MECHNANICS
Normal file
28
src/com/Torvald/Terrarum/MECHNANICS
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
* Weapon tier
|
||||
|
||||
Natural / Common Stone > Copper > Iron > Silver > Titanium
|
||||
Forging ------------> Steel -------^
|
||||
Exotic ('elven') Glass Aurichalcum
|
||||
Special (something 'adamant') ??? (Use material spec of CNT, tensile strength 180 GPa)
|
||||
|
||||
|
||||
* Metal graphics
|
||||
|
||||
Gold: Hue 43, low Saturation
|
||||
Aurichalcum: Hue 43, mid-high Saturation
|
||||
Copper: Hue 33,
|
||||
Copper rust: Hue 160
|
||||
Iron rust: Hue 15
|
||||
|
||||
|
||||
* Size variation
|
||||
|
||||
Race base weapon/tool size <- 10 [kg]
|
||||
Size tolerance <- (50% * str/1000), or say, 20%
|
||||
|
||||
If the size is bigger than tolerable, weapon speed severely slows down, tools become unusable
|
||||
if use time >= 0.75 second, the weapon/tool cannot be equipped.
|
||||
Small weapons gains no (dis)advantage, tools become unusable
|
||||
|
||||
Crafted tool/weapon size is dependent on the baseRaceMass.
|
||||
466
src/com/Torvald/Terrarum/MapDrawer/LightmapRenderer.java
Normal file
466
src/com/Torvald/Terrarum/MapDrawer/LightmapRenderer.java
Normal file
@@ -0,0 +1,466 @@
|
||||
package com.Torvald.Terrarum.MapDrawer;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.jme3.math.FastMath;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
public class LightmapRenderer {
|
||||
|
||||
/**
|
||||
* 8-Bit RGB values
|
||||
*/
|
||||
private static int[][] staticLightMap = new int[Game.map.height][Game.map.width];
|
||||
|
||||
/**
|
||||
* For entities that emits light (e.g. Player with shine potion)
|
||||
*/
|
||||
private static ArrayList<LightmapLantern> lanterns = new ArrayList<>();
|
||||
|
||||
private static final int AIR_OPACITY = 8;
|
||||
private static final int NON_AIR_OPACITY = 32;
|
||||
|
||||
private static final int AIR = 0;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Stores current light map as image.
|
||||
* WILL BE PURGED in every single round of light calculation.
|
||||
*/
|
||||
//private static Graphics lightMapGraphicsInstance;
|
||||
|
||||
|
||||
public LightmapRenderer() {
|
||||
|
||||
}
|
||||
|
||||
public static void addLantern(int x, int y, LightmapLantern lantern) {
|
||||
// TODO check for duplicates
|
||||
lanterns.add(lantern);
|
||||
}
|
||||
|
||||
public static void removeLantern(int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
public static void renderLightMap() {
|
||||
|
||||
int for_y_start = div16(MapCamera.getCameraY());
|
||||
int for_x_start = div16(MapCamera.getCameraX());
|
||||
|
||||
int for_y_end = clampHTile(for_y_start + div16(MapCamera.getRenderHeight()) + 2);
|
||||
int for_x_end = clampWTile(for_x_start + div16(MapCamera.getRenderWidth()) + 2);
|
||||
|
||||
/**
|
||||
* Updating order:
|
||||
* +-----+ +-----+ +-----+ +-----+
|
||||
* |1 | | 1| |3 | | 3|
|
||||
* | 2 | > | 2 | > | 2 | > | 2 |
|
||||
* | 3| |3 | | 1| |1 |
|
||||
* +-----+ +-----+ +-----+ +-----+
|
||||
* round: 1 2 3 4
|
||||
* for all staticLightMap[y][x]
|
||||
*/
|
||||
|
||||
// Round 1
|
||||
purgePartOfLightmap(for_x_start, for_y_start, for_x_end, for_y_end);
|
||||
|
||||
//System.out.println(for_x_start);
|
||||
//System.out.println(for_x_end);
|
||||
|
||||
for (int y = for_y_start; y < for_y_end; y++) {
|
||||
for (int x = for_x_start; x < for_x_end; x++) {
|
||||
calculateAndSet(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--) {
|
||||
calculateAndSet(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--) {
|
||||
calculateAndSet(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++) {
|
||||
calculateAndSet(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void draw(Graphics g) {
|
||||
int for_x_start = MapCamera.getRenderStartX();
|
||||
int for_y_start = MapCamera.getRenderStartY();
|
||||
int for_x_end = MapCamera.getRenderEndX();
|
||||
int for_y_end = MapCamera.getRenderEndY();
|
||||
|
||||
// draw
|
||||
for (int y = for_y_start; y < for_y_end; y++) {
|
||||
for (int x = for_x_start; x < for_x_end; x++) {
|
||||
// smooth
|
||||
if (Game.screenZoom >= 1 && ((boolean) Game.gameConfig.get("smoothlighting"))) {
|
||||
int thisLightLevel = staticLightMap[y][x];
|
||||
if (y > 0 && x < for_x_end && thisLightLevel == 0 && staticLightMap[y - 1][x] == 0) {
|
||||
// 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 * Game.screenZoom)
|
||||
, Math.round(y * TSIZE * Game.screenZoom)
|
||||
, FastMath.ceil(TSIZE * Game.screenZoom) * zeroLevelCounter
|
||||
, FastMath.ceil(TSIZE * Game.screenZoom)
|
||||
);
|
||||
|
||||
x += (zeroLevelCounter - 1);
|
||||
}
|
||||
else {
|
||||
/** a
|
||||
* +-+-+
|
||||
* |i|j|
|
||||
* b +-+-+ c
|
||||
* |k|l|
|
||||
* +-+-+
|
||||
* d
|
||||
*/
|
||||
int a = (y == 0) ? thisLightLevel
|
||||
: (y == Game.map.height - 1) ? thisLightLevel
|
||||
: Math.max(staticLightMap[y][x]
|
||||
, staticLightMap[y - 1][x]);
|
||||
int d = (y == 0) ? thisLightLevel
|
||||
: (y == Game.map.height - 1) ? thisLightLevel
|
||||
: Math.max(staticLightMap[y][x]
|
||||
, staticLightMap[y + 1][x]);
|
||||
int b = (x == 0) ? thisLightLevel
|
||||
: (x == Game.map.width - 1) ? thisLightLevel
|
||||
: Math.max(staticLightMap[y][x]
|
||||
, staticLightMap[y][x - 1]);
|
||||
int c = (x == 0) ? thisLightLevel
|
||||
: (x == Game.map.width - 1) ? thisLightLevel
|
||||
: Math.max(staticLightMap[y][x]
|
||||
, staticLightMap[y][x + 1]);
|
||||
int[] colourMapItoL = new int[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(new Color(colourMapItoL[iy * 2 + ix]));
|
||||
|
||||
g.fillRect(
|
||||
Math.round(x * TSIZE * Game.screenZoom) + (ix * TSIZE / 2 * Game.screenZoom)
|
||||
, Math.round(y * TSIZE * Game.screenZoom) + (iy * TSIZE / 2 * Game.screenZoom)
|
||||
, FastMath.ceil(TSIZE * Game.screenZoom / 2)
|
||||
, FastMath.ceil(TSIZE * Game.screenZoom / 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Retro
|
||||
else {
|
||||
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(new Color(staticLightMap[y][x]));
|
||||
g.fillRect(
|
||||
Math.round(x * TSIZE * Game.screenZoom)
|
||||
, Math.round(y * TSIZE * Game.screenZoom)
|
||||
, FastMath.ceil(TSIZE * Game.screenZoom) * sameLevelCounter
|
||||
, FastMath.ceil(TSIZE * Game.screenZoom)
|
||||
);
|
||||
|
||||
x += (sameLevelCounter - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void calculateAndSet(int x, int y){
|
||||
if (!outOfBounds(x, y)){
|
||||
|
||||
byte[][] layerTerrain = Game.map.getTerrainArray();
|
||||
byte[][] layerWall = Game.map.getWallArray();
|
||||
int lightColor;
|
||||
|
||||
int thisTerrain = layerTerrain[y][x];
|
||||
int thisWall = layerWall[y][x];
|
||||
|
||||
// open air. TODO replace with globalLightLevel
|
||||
if (thisTerrain == AIR && thisWall == AIR) {
|
||||
lightColor = 0xffffff; //0xe8fbff;
|
||||
}
|
||||
//else if (thisTerrain == 1) {
|
||||
// lightColor = 0xff0000;
|
||||
//}
|
||||
// TODO lantern + light-emitter, light-emitter, lantern
|
||||
else {
|
||||
int[] bgrVal = new int[3]; // {B, G, R}
|
||||
|
||||
// test for each R, G, B channel
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int brightest = 0;
|
||||
|
||||
//get brightest of nearby 4 tiles
|
||||
int nearby = 0;
|
||||
findNearbyBrightest:
|
||||
for (int yoff = -1; yoff <= 1; yoff++) {
|
||||
for (int xoff = -1; xoff <= 1; xoff++) {
|
||||
/**
|
||||
* filter for 'v's as:
|
||||
* +-+-+-+
|
||||
* | |v| |
|
||||
* +-+-+-+
|
||||
* |v| |v|
|
||||
* +-+-+-+
|
||||
* | |v| |
|
||||
* +-+-+-+
|
||||
*/
|
||||
if (xoff != yoff && -xoff != yoff) {
|
||||
if (!outOfMapBounds(x + xoff, y + yoff)) {
|
||||
nearby = getRaw(staticLightMap[y + yoff][x + xoff], i);
|
||||
}
|
||||
|
||||
if (nearby > brightest) {
|
||||
brightest = nearby;
|
||||
}
|
||||
|
||||
if (brightest == 0xFF) break findNearbyBrightest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//return: brightest - opacity
|
||||
if (thisTerrain == AIR) {
|
||||
bgrVal[i] = darken(brightest, AIR_OPACITY);
|
||||
}
|
||||
else {
|
||||
bgrVal[i] = darken(brightest, NON_AIR_OPACITY);
|
||||
}
|
||||
}
|
||||
|
||||
// construct lightColor from bgrVal
|
||||
lightColor = constructRGB(
|
||||
bgrVal[OFFSET_R]
|
||||
, bgrVal[OFFSET_G]
|
||||
, bgrVal[OFFSET_B]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
//return lightColor;
|
||||
staticLightMap[y][x] = lightColor;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Out of bounds of lightMap");
|
||||
}
|
||||
}
|
||||
|
||||
//public Graphics getGraphics() {
|
||||
// return lightMapGraphicsInstance;
|
||||
//}
|
||||
|
||||
/*private static int darken(int RGB, int darken) {
|
||||
if (darken < 0 || darken > 0xFF) { throw new IllegalArgumentException("darken: out of range"); }
|
||||
|
||||
float r = getR(RGB) * ((0xFF - darken) / 0xFFf);
|
||||
float g = getG(RGB) * ((0xFF - darken) / 0xFFf);
|
||||
float b = getB(RGB) * ((0xFF - darken) / 0xFFf);
|
||||
|
||||
return constructRGB(r, g, b);
|
||||
}*/
|
||||
|
||||
private static int darken(int data, int darken) {
|
||||
if (darken < 0 || darken > 0xFF) { throw new IllegalArgumentException("darken: out of range"); }
|
||||
|
||||
return clampZero(data - darken);
|
||||
}
|
||||
|
||||
private static int getRawR(int RGB) {
|
||||
return (RGB >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
private static int getRawG(int RGB) {
|
||||
return (RGB >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
private static int getRawB(int RGB) {
|
||||
return RGB & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param RGB
|
||||
* @param offset 2 = R, 1 = G, 0 = B
|
||||
* @return
|
||||
*/
|
||||
private static int getRaw(int RGB, int offset) {
|
||||
if (offset < 0 || offset > 2) throw new IllegalArgumentException("Offset out of range");
|
||||
return (RGB >> (8 * offset)) & 0xFF;
|
||||
}
|
||||
|
||||
private static float getR(int rgb) {
|
||||
return getRawR(rgb) / 255f;
|
||||
}
|
||||
|
||||
private static float getG(int rgb) {
|
||||
return getRawG(rgb) / 255f;
|
||||
}
|
||||
|
||||
private static float getB(int rgb) {
|
||||
return getRawB(rgb) / 255f;
|
||||
}
|
||||
|
||||
private static int constructRGB(int r, int g, int b) {
|
||||
if (r < 0 || r > 0xFF) { throw new IllegalArgumentException("Red: out of range"); }
|
||||
if (g < 0 || g > 0xFF) { throw new IllegalArgumentException("Green: out of range"); }
|
||||
if (b < 0 || b > 0xFF) { throw new IllegalArgumentException("Blue: out of range"); }
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
private static int constructRGB(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 * 0xFF);
|
||||
int intG = Math.round(g * 0xFF);
|
||||
int intB = Math.round(b * 0xFF);
|
||||
|
||||
return constructRGB(intR, intG, intB);
|
||||
}
|
||||
|
||||
private static int colourLinearMix(int colA, int colB) {
|
||||
int r = (getRawR(colA) + getRawR(colB)) >> 1;
|
||||
int g = (getRawG(colA) + getRawG(colB)) >> 1;
|
||||
int b = (getRawB(colA) + getRawB(colB)) >> 1;
|
||||
return constructRGB(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 boolean outOfBounds(int x, int y){
|
||||
return ( x < 0 || y < 0 || x >= Game.map.width || y >= 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) {
|
||||
if (i < 0) return 0;
|
||||
else return i;
|
||||
}
|
||||
|
||||
public static int[][] getStaticLightMap() {
|
||||
return staticLightMap;
|
||||
}
|
||||
|
||||
public static int getValueFromMap(int x, int y) {
|
||||
return staticLightMap[y][x];
|
||||
}
|
||||
|
||||
private static void purgePartOfLightmap(int x1, int y1, int x2, int y2) {
|
||||
for (int y = y1; y < y2; y++) {
|
||||
for (int x = x1; x < x2; x++) {
|
||||
if (!outOfMapBounds(x, y)) {
|
||||
staticLightMap[y][x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int clampWTile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > Game.map.width) {
|
||||
return Game.map.width;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clampHTile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > Game.map.height) {
|
||||
return Game.map.height;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LightmapLantern {
|
||||
int x, y;
|
||||
int intensity;
|
||||
|
||||
public LightmapLantern(int x, int y, int intensity) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.intensity = intensity;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getIntensity() {
|
||||
return intensity;
|
||||
}
|
||||
}
|
||||
335
src/com/Torvald/Terrarum/MapDrawer/MapCamera.java
Normal file
335
src/com/Torvald/Terrarum/MapDrawer/MapCamera.java
Normal file
@@ -0,0 +1,335 @@
|
||||
package com.Torvald.Terrarum.MapDrawer;
|
||||
|
||||
import com.Torvald.Terrarum.Actors.Player;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.GameMap.GameMap;
|
||||
import com.Torvald.Terrarum.GameMap.MapLayer;
|
||||
import com.jme3.math.FastMath;
|
||||
import org.newdawn.slick.*;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-19.
|
||||
*/
|
||||
public class MapCamera {
|
||||
|
||||
private static GameMap map;
|
||||
private static Player player;
|
||||
|
||||
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 = 0;
|
||||
private static final int TERRAIN = 1;
|
||||
private static final int WIRE = 2;
|
||||
|
||||
private static int renderWidth;
|
||||
private static int renderHeight;
|
||||
|
||||
private static final int TILE_AIR = 0;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @param map
|
||||
* @param tileSize
|
||||
*/
|
||||
public MapCamera(GameMap map, int tileSize) throws SlickException {
|
||||
this.map = map;
|
||||
player = Game.getPlayer();
|
||||
|
||||
tilesWall = new SpriteSheet("./res/graphics/terrain/wall.png"
|
||||
, TSIZE
|
||||
, TSIZE
|
||||
);
|
||||
|
||||
tilesTerrain = new SpriteSheet("./res/graphics/terrain/terrainplusplus.png"
|
||||
, TSIZE
|
||||
, TSIZE
|
||||
);
|
||||
|
||||
tilesWire = new SpriteSheet("./res/graphics/terrain/wire.png"
|
||||
, TSIZE
|
||||
, TSIZE
|
||||
);
|
||||
|
||||
tilesetBook = new SpriteSheet[9];
|
||||
tilesetBook[WALL] = tilesWall;
|
||||
tilesetBook[TERRAIN] = tilesTerrain;
|
||||
tilesetBook[WIRE] = tilesWire;
|
||||
}
|
||||
|
||||
public static void update(GameContainer gc, int delta_t) {
|
||||
renderWidth = FastMath.ceil(Terrarum.WIDTH / Game.screenZoom);
|
||||
renderHeight = FastMath.ceil(Terrarum.HEIGHT / Game.screenZoom);
|
||||
|
||||
// position - (WH / 2)
|
||||
cameraX = clamp(
|
||||
Math.round(player.pointedPosX() - (renderWidth / 2))
|
||||
, map.width * TSIZE - renderWidth
|
||||
);
|
||||
cameraY = clamp(
|
||||
Math.round(player.pointedPosY() - (renderHeight / 2))
|
||||
, map.height * TSIZE - renderHeight
|
||||
);
|
||||
}
|
||||
|
||||
public static void render(GameContainer gc, Graphics g) {
|
||||
/**
|
||||
* render to camera
|
||||
*/
|
||||
drawTiles(WALL);
|
||||
drawTiles(TERRAIN);
|
||||
}
|
||||
|
||||
private static void drawTiles(int mode) {
|
||||
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);
|
||||
|
||||
MapLayer currentLayer = (mode % 3 == WALL) ? map.getLayerWall()
|
||||
: (mode % 3 == TERRAIN) ? map.getLayerTerrain()
|
||||
: map.getLayerWire();
|
||||
|
||||
// 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 = currentLayer.getTile(x, y);
|
||||
int thisTerrainTile = map.getTileFromTerrain(x, y);
|
||||
|
||||
// draw
|
||||
if (
|
||||
|
||||
(
|
||||
( // wall and not blocked
|
||||
(mode == WALL) && (!isOpaque(thisTerrainTile))
|
||||
)
|
||||
||
|
||||
(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)
|
||||
))
|
||||
) {
|
||||
|
||||
if (mode == TERRAIN) {
|
||||
int nearbyTilesInfo = getNearbyTilesInfo(x, y, TILE_AIR);
|
||||
|
||||
int thisTileX = nearbyTilesInfo;
|
||||
int thisTileY = thisTile;
|
||||
|
||||
drawTile(TERRAIN, x, y, thisTileX, thisTileY);
|
||||
}
|
||||
else {
|
||||
drawTile(mode, x, y, mod16(thisTile), div16(thisTile));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tilesetBook[mode].endUse();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return [0-15] 1: up, 2: right, 4: down, 8: left
|
||||
*/
|
||||
private static int getNearbyTilesInfo(int x, int y, int mark) {
|
||||
int[] nearbyTiles = new int[4];
|
||||
if (x == 0) { nearbyTiles[NEARBY_TILE_KEY_LEFT] = 0; }
|
||||
if (x == map.width - 1) { nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 0; }
|
||||
if (y == 0) { nearbyTiles[NEARBY_TILE_KEY_UP] = 0; }
|
||||
if (y == map.height - 1) { nearbyTiles[NEARBY_TILE_KEY_DOWN] = 0; }
|
||||
try {
|
||||
nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFromTerrain(x, y - 1);
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFromTerrain(x, y + 1);
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFromTerrain(x - 1, y);
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFromTerrain(x + 1, y);
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) { }
|
||||
|
||||
int ret = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (nearbyTiles[i] == mark) {
|
||||
ret += (1 << i); // write 1, 2, 4, 8 for i = 0, 1, 2, 3
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
private static void drawTile(int mode, int tilewisePosX, int tilewisePosY, int sheetX, int sheetY) {
|
||||
if (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 * Game.screenZoom)
|
||||
, Math.round(tilewisePosY * TSIZE * Game.screenZoom)
|
||||
, FastMath.ceil(TSIZE * Game.screenZoom)
|
||||
, FastMath.ceil(TSIZE * Game.screenZoom)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static int div16(int x) {
|
||||
return (x & 0x7FFF_FFFF) >> 4;
|
||||
}
|
||||
|
||||
private static int mod16(int x) {
|
||||
return x & 0b1111;
|
||||
}
|
||||
|
||||
private static int quantise16(int x) {
|
||||
return (x & 0xFFFF_FFF0);
|
||||
}
|
||||
|
||||
private static int clampW(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > map.width * TSIZE) {
|
||||
return map.width * TSIZE;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clampH(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > map.height * TSIZE) {
|
||||
return map.height * TSIZE;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clampWTile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > map.width) {
|
||||
return map.width;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clampHTile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > map.height) {
|
||||
return map.height;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clamp(int x, int lim) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > lim) {
|
||||
return lim;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static Image getTileByIndex(SpriteSheet s, int i) {
|
||||
return s.getSprite(i % 16, i / 16);
|
||||
}
|
||||
|
||||
private static boolean isOpaque(int x) {
|
||||
return (x >= 1 && x <= 38)
|
||||
|| (x >= 41 && x <= 44)
|
||||
|| (x >= 46 && x <= 47)
|
||||
|| (x >= 64 && x <= 86)
|
||||
|| (x >= 88 && x <= 116);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
58
src/com/Torvald/Terrarum/MapDrawer/MapDrawer.java
Normal file
58
src/com/Torvald/Terrarum/MapDrawer/MapDrawer.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.Torvald.Terrarum.MapDrawer;
|
||||
|
||||
import com.Torvald.Terrarum.*;
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.GameMap.GameMap;
|
||||
import org.newdawn.slick.*;
|
||||
import org.newdawn.slick.geom.Rectangle;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public class MapDrawer {
|
||||
|
||||
private static SpriteSheet mapTileMap;
|
||||
private static SpriteSheet wallTileMap;
|
||||
|
||||
public static final int TILE_SIZE = 16;
|
||||
public static final int ENVCOLOUR_MAX = 128;
|
||||
|
||||
private static int envmap = 64;
|
||||
private static Rectangle envOverlay;
|
||||
private static Image envOverlayColourmap;
|
||||
|
||||
public MapDrawer(GameMap map) throws SlickException {
|
||||
mapTileMap = new SpriteSheet("./res/graphics/terrain/terrain.png", TILE_SIZE, TILE_SIZE);
|
||||
wallTileMap = new SpriteSheet("./res/graphics/terrain/wall.png", TILE_SIZE, TILE_SIZE);
|
||||
|
||||
new MapCamera(map, TILE_SIZE);
|
||||
|
||||
Rectangle envOverlay = new Rectangle(
|
||||
MapCamera.getCameraX() * Game.screenZoom
|
||||
, MapCamera.getCameraY() * Game.screenZoom
|
||||
, Terrarum.WIDTH
|
||||
, Terrarum.HEIGHT
|
||||
);
|
||||
|
||||
System.gc();
|
||||
}
|
||||
|
||||
public static void update(GameContainer gc, int delta_t) {
|
||||
MapCamera.update(gc, delta_t);
|
||||
}
|
||||
|
||||
public static void render(GameContainer gc, Graphics g) {
|
||||
MapCamera.render(gc, g);
|
||||
}
|
||||
|
||||
public static void drawEnvOverlay(Graphics g) {
|
||||
envOverlay.setX(MapCamera.getCameraX() * Game.screenZoom);
|
||||
envOverlay.setY(MapCamera.getCameraY() * Game.screenZoom);
|
||||
envOverlay.setSize(Terrarum.WIDTH * Game.screenZoom
|
||||
, Terrarum.HEIGHT * Game.screenZoom
|
||||
);
|
||||
|
||||
// Color[] colourTable = getGradientColour(WorldTime.elapsedSeconds());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.Torvald.Terrarum.MapGenerator;
|
||||
|
||||
import com.Torvald.Rand.HighQualityRandom;
|
||||
|
||||
public class FloatingIslandsPreset {
|
||||
|
||||
public static int presets = 5;
|
||||
|
||||
static int[][] generatePreset(HighQualityRandom random) {
|
||||
int index = random.nextInt(presets);
|
||||
return generatePreset(index, random);
|
||||
}
|
||||
|
||||
static int[][] generatePreset(int index, HighQualityRandom random){
|
||||
if (index == 0){
|
||||
return processPreset(random, FloatingIslePreset01.data, FloatingIslePreset01.w, FloatingIslePreset01.h);
|
||||
}
|
||||
else if (index == 1){
|
||||
return processPreset(random, FloatingIslePreset02.data, FloatingIslePreset02.w, FloatingIslePreset02.h);
|
||||
}
|
||||
else if (index == 2){
|
||||
return processPreset(random, FloatingIslePreset03.data, FloatingIslePreset03.w, FloatingIslePreset03.h);
|
||||
}
|
||||
else if (index == 3){
|
||||
return processPreset(random, FloatingIslePreset04.data, FloatingIslePreset04.w, FloatingIslePreset04.h);
|
||||
}
|
||||
else if (index == 4){
|
||||
return processPreset(random, FloatingIslePreset05.data, FloatingIslePreset05.w, FloatingIslePreset05.h);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int[][] processPreset(HighQualityRandom random, int[] preset, int w, int h){
|
||||
int[][] temp = new int[h][w];
|
||||
int counter = 0;
|
||||
boolean mirrored = random.nextBoolean();
|
||||
|
||||
for (int i = 0; i < h; i++){
|
||||
for (int j = 0; j < w; j++){
|
||||
if (!mirrored){
|
||||
if (counter < preset.length - 1){
|
||||
temp[i][j] = preset[counter];
|
||||
counter++;
|
||||
}
|
||||
else{
|
||||
temp[i][j] = 0;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (counter < preset.length - 1){
|
||||
temp[i][w - 1 - j] = preset[counter];
|
||||
counter++;
|
||||
}
|
||||
else{
|
||||
temp[i][w - 1 - j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
867
src/com/Torvald/Terrarum/MapGenerator/MapGenerator.java
Normal file
867
src/com/Torvald/Terrarum/MapGenerator/MapGenerator.java
Normal file
@@ -0,0 +1,867 @@
|
||||
package com.Torvald.Terrarum.MapGenerator;
|
||||
|
||||
import com.Torvald.Rand.HighQualityRandom;
|
||||
import com.Torvald.Terrarum.GameMap.GameMap;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
|
||||
public class MapGenerator {
|
||||
|
||||
@NotNull private static GameMap map;
|
||||
private static HighQualityRandom random;
|
||||
//private static float[] noiseArray;
|
||||
@NotNull private static long seed;
|
||||
@NotNull private static int width;
|
||||
@NotNull private static int height;
|
||||
|
||||
private static int[] heightMap;
|
||||
|
||||
private static int dirtThickness;
|
||||
private static int terrainHeightFromZeroPoint;
|
||||
private static int minimumFloatingIsleHeight;
|
||||
|
||||
private static final float noiseGradientStart = 0.67f;
|
||||
private static final float noiseGradientEnd = 0.56f;
|
||||
private static final float noiseGrdCaveEnd = 0.54f;
|
||||
|
||||
private static final float floatingIslandsProb = 0.63f;
|
||||
|
||||
private static final int HILL_WIDTH = 256; // power of two!
|
||||
|
||||
private static final int OCEAN_WIDTH = 200;
|
||||
private static int GLACIER_MOUNTAIN_WIDTH;
|
||||
private static final int GLACIER_MOUNTAIN_HEIGHT = 300;
|
||||
|
||||
private static final int MAX_HILL_HEIGHT = 200;
|
||||
|
||||
private static final byte AIR = 0;
|
||||
|
||||
private static final byte COPPER = 16;
|
||||
private static final byte IRON = 17;
|
||||
private static final byte GOLD = 18;
|
||||
private static final byte SILVER = 19;
|
||||
|
||||
private static final byte ILMENITE = 20;
|
||||
private static final byte AURICHALCUM = 21;
|
||||
|
||||
private static final byte DIAMOND = 22;
|
||||
private static final byte RUBY = 23;
|
||||
private static final byte EMERALD = 24;
|
||||
private static final byte SAPPHIRE = 25;
|
||||
private static final byte TOPAZ = 26;
|
||||
private static final byte AMETHYST = 27;
|
||||
|
||||
private static final byte DIRT = 2;
|
||||
private static final byte GRAVEL = 15;
|
||||
private static final byte SAND = 14;
|
||||
private static final byte STONE = 1;
|
||||
|
||||
private static final byte SNOW = 28;
|
||||
private static final byte ICE_FRAGILE = 29;
|
||||
private static final byte ICE_NATURAL = 30;
|
||||
|
||||
@NotNull private static int worldOceanPosition;
|
||||
private static final int TYPE_OCEAN_LEFT = 0;
|
||||
private static final int TYPE_OCEAN_RIGHT = 1;
|
||||
|
||||
public static void attachMap(GameMap map) {
|
||||
MapGenerator.map = map;
|
||||
width = map.width;
|
||||
height = map.height;
|
||||
|
||||
dirtThickness = (int) (100 * height / 1024f);
|
||||
minimumFloatingIsleHeight = (int) (25 * (height / 1024f));
|
||||
terrainHeightFromZeroPoint = height / 4;
|
||||
GLACIER_MOUNTAIN_WIDTH = Math.round(900 * (width / 8192f));
|
||||
}
|
||||
|
||||
public static void setSeed(long seed) {
|
||||
MapGenerator.seed = seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate terrain and override attached map
|
||||
*/
|
||||
public static void generateMap() {
|
||||
random = new HighQualityRandom(seed);
|
||||
System.out.println("[MapGenerator] Seed: " + seed);
|
||||
|
||||
worldOceanPosition = random.nextInt() & 0x1; // 0 or 1
|
||||
|
||||
heightMap = raise2(MAX_HILL_HEIGHT / 2);
|
||||
generateOcean(heightMap);
|
||||
placeGlacierMount(heightMap);
|
||||
heightMapToObjectMap(heightMap);
|
||||
|
||||
/*carveByMap(
|
||||
generate2DSimplexNoiseWorldSize(2.5f, 1.666f)
|
||||
, 1
|
||||
, AIR
|
||||
, "Carving out cave..."
|
||||
);*/
|
||||
|
||||
/*fillByMapInverseGradFilter(
|
||||
generate2DSimplexNoiseWorldSize(2.5f, 2.5f)
|
||||
, 1.02f
|
||||
, DIRT
|
||||
, STONE
|
||||
, "Planting stones on dirt layers..."
|
||||
);*/
|
||||
/*fillByMapInverseGradFilter(
|
||||
generate2DSimplexNoiseWorldSize(2.5f, 2.5f)
|
||||
, 0.98f
|
||||
, STONE
|
||||
, DIRT
|
||||
, "Planting dirts..."
|
||||
);
|
||||
fillByMapInverseGradFilter(
|
||||
generate2DSimplexNoiseWorldSize(2.5f, 2.5f)
|
||||
, 0.92f
|
||||
, STONE
|
||||
, GRAVEL
|
||||
, "Planting gravels..."
|
||||
);*/
|
||||
|
||||
/**
|
||||
* Plant ores
|
||||
*/
|
||||
/*fillByMap(
|
||||
generate2DSimplexNoiseWorldSize(5, 5)
|
||||
, 0.78f
|
||||
, STONE
|
||||
, DIAMOND
|
||||
, "Planting diamonds..."
|
||||
);
|
||||
|
||||
byte[] berylsArray = {RUBY, EMERALD, SAPPHIRE, TOPAZ, AMETHYST};
|
||||
fillByMap(
|
||||
generate2DSimplexNoiseWorldSize(5, 5)
|
||||
, 0.8f
|
||||
, STONE
|
||||
, berylsArray
|
||||
, "Planting beryls..."
|
||||
);
|
||||
|
||||
fillByMap(
|
||||
generate2DSimplexNoiseWorldSize(5, 5)
|
||||
, 0.80f
|
||||
, STONE
|
||||
, GOLD
|
||||
, "Planting golds..."
|
||||
);
|
||||
fillByMap(
|
||||
generate2DSimplexNoiseWorldSize(5, 5)
|
||||
, 0.866f
|
||||
, STONE
|
||||
, IRON
|
||||
, "Planting irons..."
|
||||
);
|
||||
fillByMap(
|
||||
generate2DSimplexNoiseWorldSize(5, 5)
|
||||
, 0.88f
|
||||
, STONE
|
||||
, COPPER
|
||||
, "Planting coppers..."
|
||||
);*/
|
||||
|
||||
/** TODO Cobaltite, Ilmenite, Aurichalcum (and possibly pitchblende?) **/
|
||||
|
||||
/*fillByMap(
|
||||
generate2DSimplexNoiseWorldSize(5, 5)
|
||||
, 1.21f
|
||||
, STONE
|
||||
, COAL
|
||||
, "Planting coals..."
|
||||
);*/
|
||||
|
||||
//flood
|
||||
floodBottomLava();
|
||||
|
||||
//plant
|
||||
plantGrass();
|
||||
|
||||
//post-process
|
||||
generateFloatingIslands();
|
||||
//fillOcean();
|
||||
|
||||
//wire layer
|
||||
for (int i = 0; i < height; i++) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
map.getWireArray()[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Free some memories
|
||||
System.gc();
|
||||
}
|
||||
|
||||
/* 1. Raise */
|
||||
|
||||
private static float[][] generate2DSimplexNoiseWorldSize(float xDensity, float yDensity) {
|
||||
return generate2DSimplexNoise(width, height, xDensity, yDensity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate 2D array of simplex noise.
|
||||
* @param sizeX
|
||||
* @param sizeY
|
||||
* @param xDensity higher == dense (smaller blob), lower == sparse (larger blob)
|
||||
* @param yDensity higher == dense (smaller blob), lower == sparse (larger blob)
|
||||
* @return matrix in ![x][y]!
|
||||
*/
|
||||
private static float[][] generate2DSimplexNoise(int sizeX, int sizeY, float xDensity, float yDensity){
|
||||
SimplexNoise simplexNoise = new SimplexNoise(HILL_WIDTH, 0.1f, seed ^ random.nextLong());
|
||||
|
||||
float xStart=0;
|
||||
float yStart=0;
|
||||
|
||||
/** higher = denser.
|
||||
* Recommended: (width or height) * 3
|
||||
*/
|
||||
float xEnd=height * xDensity;
|
||||
float yEnd=width * yDensity;
|
||||
|
||||
float[][] result=new float[sizeY][sizeX];
|
||||
|
||||
for(int i=0;i<sizeY;i++){
|
||||
for(int j=0;j<sizeX;j++){
|
||||
int x=(int)(xStart+i*((xEnd-xStart)/sizeX));
|
||||
int y=(int)(yStart+j*((yEnd-yStart)/sizeY));
|
||||
result[i][j]=(float) (0.5*(1+simplexNoise.getNoise(x,y)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static float[] generateWhiteNoiseArray(long seed) {
|
||||
float[] noiseArray = new float[MapGenerator.width + 1];
|
||||
for (int i = 0; i < noiseArray.length; i++) {
|
||||
noiseArray[i] = random.nextFloat();
|
||||
}
|
||||
|
||||
return noiseArray;
|
||||
}
|
||||
|
||||
private static int[] generateOcean(int[] noiseArrayLocal) {
|
||||
int oceanLeftP1 = noiseArrayLocal[OCEAN_WIDTH];
|
||||
int oceanRightP1 = noiseArrayLocal[noiseArrayLocal.length - OCEAN_WIDTH];
|
||||
|
||||
/**
|
||||
* Add ocean so that:
|
||||
*
|
||||
* +1| - -
|
||||
* 0| - -- ...
|
||||
* -1|______ -
|
||||
*
|
||||
* interpolated to
|
||||
*
|
||||
* +1| - -
|
||||
* 0| _--- -- ...
|
||||
* -1|__- -
|
||||
*
|
||||
* ↑-- Rough, white noise
|
||||
*
|
||||
* -1 means -MAX_HILL_HEIGHT
|
||||
*/
|
||||
for (int i = 0; i < OCEAN_WIDTH; i++) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
noiseArrayLocal[i] = Math.round(
|
||||
interpolateCosine(
|
||||
Math.round((float) (i) / OCEAN_WIDTH)
|
||||
, -MAX_HILL_HEIGHT, oceanLeftP1
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
noiseArrayLocal[noiseArrayLocal.length - OCEAN_WIDTH + i] = Math.round(
|
||||
interpolateCosine(
|
||||
Math.round((float) (i) / OCEAN_WIDTH)
|
||||
, oceanRightP1, -MAX_HILL_HEIGHT
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Ocean position were not set correctly.");
|
||||
}
|
||||
}
|
||||
|
||||
return noiseArrayLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* <a href>http://freespace.virgin.net/hugo.elias/models/m_perlin.htm</a>
|
||||
* @param maxval_div_2 max height (deviation from zero) divided by two.
|
||||
* @return noise array with range of [-maxval, maxval]
|
||||
*/
|
||||
private static int[] raise2(int maxval_div_2) {
|
||||
|
||||
int finalPerlinAmp = maxval_div_2; // 1 + 1/2 + 1/4 + 1/8 + ... == 2
|
||||
int perlinOctaves = FastMath.intLog2(maxval_div_2) +1 -1; // max: for every 2nd node
|
||||
|
||||
int[] perlinMap = new int[width]; // [-2 * finalPerlinAmp, finalPerlinAmp]
|
||||
|
||||
// assert
|
||||
if ((HILL_WIDTH) >>> (perlinOctaves - 1) == 0) {
|
||||
throw new RuntimeException("sample width of zero detected.");
|
||||
}
|
||||
|
||||
// sample noise and add
|
||||
for (int oct = 1; oct <= perlinOctaves; oct++) {
|
||||
// perlinAmp: 16364 -> 8192 -> 4096 -> 2048 -> ...
|
||||
// This applies persistence of 1/2
|
||||
int perlinAmp = finalPerlinAmp >>> (oct - 1);
|
||||
|
||||
int perlinSampleDist = (HILL_WIDTH) >>> (oct - 1);
|
||||
|
||||
// sample first
|
||||
int[] perlinSamples = new int[width / perlinSampleDist + 1];
|
||||
for (int sample = 0; sample < perlinSamples.length; sample++) {
|
||||
perlinSamples[sample] = random.nextInt(perlinAmp * 2) - perlinAmp;
|
||||
}
|
||||
|
||||
// add interpolated value to map
|
||||
for (int i = 0; i < perlinMap.length; i++) {
|
||||
int perlinPointLeft = perlinSamples[i / perlinSampleDist];
|
||||
int perlinPointRight = perlinSamples[i / perlinSampleDist + 1];
|
||||
|
||||
perlinMap[i] += Math.round(
|
||||
interpolateCosine(
|
||||
((float) (i % perlinSampleDist)) / perlinSampleDist
|
||||
, perlinPointLeft, perlinPointRight
|
||||
)
|
||||
// using cosine; making tops rounded
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < 1; k++) {
|
||||
for (int i = 0; i < perlinMap.length; i++) {
|
||||
// averaging smoothing
|
||||
if (i > 1 && i < perlinMap.length - 2) {
|
||||
perlinMap[i] = Math.round(
|
||||
(perlinMap[i - 1] + perlinMap[i + 1]) / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// single bump removal
|
||||
for (int i = 0; i < perlinMap.length; i++) {
|
||||
if (i > 1 && i < perlinMap.length - 2) {
|
||||
int p1 = perlinMap[i - 1];
|
||||
int p2 = perlinMap[i];
|
||||
int p3 = perlinMap[i + 1];
|
||||
// _-_ / -_- -> ___ / ---
|
||||
if (p1 == p3 && p1 != p2) {
|
||||
perlinMap[i] = p1;
|
||||
}
|
||||
// -^_ -> --_
|
||||
else if (p1 > p3 && p2 > p1) {
|
||||
perlinMap[i] = p1;
|
||||
}
|
||||
// _^- -> _--
|
||||
else if (p3 > p1 && p2 > p3) {
|
||||
perlinMap[i] = p3;
|
||||
}
|
||||
// -_^ -> --^
|
||||
else if (p3 > p1 && p2 < p1) {
|
||||
perlinMap[i] = p1;
|
||||
}
|
||||
// ^_- -> ^--
|
||||
else if (p1 > p3 && p2 < p3) {
|
||||
perlinMap[i] = p1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return perlinMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* | ----
|
||||
* | ---
|
||||
* | ---
|
||||
* | --
|
||||
* | -
|
||||
* | --
|
||||
* | ---
|
||||
* | ---
|
||||
* - ----------------------------
|
||||
*
|
||||
* @param func_x
|
||||
* @return
|
||||
*/
|
||||
private static float getGlacierMountedAmplitude(int func_x) {
|
||||
if (func_x > GLACIER_MOUNTAIN_WIDTH) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
float func_y = (GLACIER_MOUNTAIN_HEIGHT / 2f)
|
||||
* (float) Math.cos(10 * func_x / (FastMath.PI * GLACIER_MOUNTAIN_WIDTH))
|
||||
+ (GLACIER_MOUNTAIN_HEIGHT / 2);
|
||||
return func_y;
|
||||
}
|
||||
}
|
||||
|
||||
private static void placeGlacierMount(int[] heightMap) {
|
||||
System.out.println("[MapGenerator] Putting glacier...");
|
||||
|
||||
// raise
|
||||
for (int i = 0; i < heightMap.length; i++) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
heightMap[i] += Math.round(getGlacierMountedAmplitude(i));
|
||||
}
|
||||
else {
|
||||
heightMap[i] += Math.round(getGlacierMountedAmplitude(heightMap.length - i - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cosine interpolation between point a and b.
|
||||
* @param x [0.0, 1.0] relative position between a and b
|
||||
* @param a leftmost point
|
||||
* @param b rightmost point
|
||||
* @return
|
||||
*/
|
||||
private static float interpolateCosine(float x, float a, float b) {
|
||||
float ft = x * FastMath.PI;
|
||||
float f = (1 - FastMath.cos(ft)) * 0.5f;
|
||||
|
||||
return (a * (1 - f) + b * f);
|
||||
}
|
||||
|
||||
private static void heightMapToObjectMap(int[] fs) {
|
||||
System.out.println("[MapGenerator] Shaping world as processed...");
|
||||
|
||||
// iterate for heightmap
|
||||
for (int x = 0; x < width; x++) {
|
||||
int medianPosition = terrainHeightFromZeroPoint;
|
||||
int pillarOffset = medianPosition - fs[x] - 1;
|
||||
|
||||
// for pillar length
|
||||
for (int i = 0; i < height - pillarOffset; i++) {
|
||||
|
||||
if (i < dirtThickness) {
|
||||
map.getTerrainArray()[i + pillarOffset][x] = DIRT;
|
||||
map.getWallArray()[i + pillarOffset][x] = DIRT;
|
||||
} else {
|
||||
map.getTerrainArray()[i + pillarOffset][x] = STONE;
|
||||
map.getWallArray()[i + pillarOffset][x] = STONE;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 2. Carve */
|
||||
|
||||
/**
|
||||
* Carve (place air block) by noisemap, inversed gradation filter applied.
|
||||
* @param map noisemap
|
||||
* @param scarcity higher = larger blob
|
||||
* @param tile
|
||||
* @param message
|
||||
*/
|
||||
private static void carveByMap(float[][] map, float scarcity, byte tile, String message) {
|
||||
System.out.println("[MapGenerator] " + message);
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
if (map[i][j] > noiseMapGetGradientQuadPoly(i, noiseGradientStart, noiseGrdCaveEnd) / scarcity) {
|
||||
MapGenerator.map.getTerrainArray()[i][j] = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill by noisemap, gradation filter applied.
|
||||
* @param map noisemap
|
||||
* @param scarcity higher = larger blob
|
||||
* @param replaceFrom
|
||||
* @param tile
|
||||
* @param message
|
||||
*/
|
||||
private static void fillByMap(float[][] map, float scarcity, byte replaceFrom, byte tile, String message) {
|
||||
System.out.println("[MapGenerator] " + message);
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
if (map[i][j] > getNoiseGradient(i, noiseGradientStart, noiseGradientEnd) / scarcity
|
||||
&& MapGenerator.map.getTileFromTerrain(j, i) == replaceFrom) {
|
||||
MapGenerator.map.getTerrainArray()[i][j] = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill by noisemap, inversed gradation filter applied.
|
||||
* @param map noisemap
|
||||
* @param scarcity higher = larger blob
|
||||
* @param replaceFrom
|
||||
* @param tile
|
||||
* @param message
|
||||
*/
|
||||
private static void fillByMapInverseGradFilter(float[][] map, float scarcity, byte replaceFrom, byte tile, String message) {
|
||||
System.out.println("[MapGenerator] " + message);
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
if (map[i][j] > getNoiseGradientInversed(i, noiseGradientEnd, noiseGradientStart) / scarcity
|
||||
&& MapGenerator.map.getTileFromTerrain(j, i) == replaceFrom) {
|
||||
MapGenerator.map.getTerrainArray()[i][j] = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill by noisemap, no filter applied. Takes
|
||||
* <p>noiseGradientStart / scarcity</p>
|
||||
* as carving threshold.
|
||||
* @param map noisemap
|
||||
* @param scarcity higher = larger blob
|
||||
* @param replaceFrom
|
||||
* @param tile
|
||||
* @param message
|
||||
*/
|
||||
private static void fillByMapNoFilter(float[][] map, float scarcity, byte replaceFrom, byte tile, String message) {
|
||||
System.out.println("[MapGenerator] " + message);
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
if (map[i][j] > noiseGradientStart / scarcity
|
||||
&& MapGenerator.map.getTileFromTerrain(j, i) == replaceFrom) {
|
||||
MapGenerator.map.getTerrainArray()[i][j] = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void fillByMap(float[][] map, float scarcity, byte replaceFrom, byte[] tile, String message) {
|
||||
System.out.println("[MapGenerator] " + message);
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
if (map[i][j] > getNoiseGradient(i, noiseGradientStart, noiseGradientEnd) / scarcity
|
||||
&& MapGenerator.map.getTileFromTerrain(j, i) == replaceFrom) {
|
||||
MapGenerator.map.getTerrainArray()[i][j]
|
||||
= tile[random.nextInt(tile.length)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float getNoiseGradient(int x, float start, float end) {
|
||||
return noiseMapGetGradientQuadPoly(x, start, end);
|
||||
}
|
||||
|
||||
private static float getNoiseGradientInversed(int x, float start, float end) {
|
||||
return noiseMapGetGradientMinusQuadPoly(x, start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quadratic polynomial
|
||||
* (16/9) * (start-end)/height^2 * (x-height)^2 + end
|
||||
* 16/9: terrain is formed from 1/4 of height.
|
||||
* 1 - (1/4) = 3/4, reverse it and square it.
|
||||
* That makes 16/9.
|
||||
*
|
||||
* Shape:
|
||||
*
|
||||
* cavity -
|
||||
* small
|
||||
* -
|
||||
* -
|
||||
* --
|
||||
* ----
|
||||
* cavity --------
|
||||
* large ----------------
|
||||
*
|
||||
* @param func_argX
|
||||
* @param start
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
private static float noiseMapGetGradientQuadPoly(int func_argX, float start, float end) {
|
||||
float graph_gradient =
|
||||
FastMath.pow(FastMath.sqr(1 - terrainHeightFromZeroPoint), -1) // 1/4 -> 3/4 -> 9/16 -> 16/9
|
||||
* (start - end) / FastMath.sqr(height)
|
||||
* FastMath.sqr(func_argX - height)
|
||||
+ end
|
||||
;
|
||||
|
||||
if (func_argX < terrainHeightFromZeroPoint) {
|
||||
return start;
|
||||
}
|
||||
else if (func_argX >= height) {
|
||||
return end;
|
||||
}
|
||||
else {
|
||||
return graph_gradient;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Double Quadratic polynomial
|
||||
* (16/9) * (start-end)/height^2 * (x-height)^2 + end
|
||||
* 16/9: terrain is formed from 1/4 of height.
|
||||
* 1 - (1/4) = 3/4, reverse it and square it.
|
||||
* That makes 16/9.
|
||||
*
|
||||
* Shape:
|
||||
*
|
||||
* cavity -
|
||||
* small
|
||||
* -
|
||||
* -
|
||||
* --
|
||||
* ----
|
||||
* cavity --------
|
||||
* large ----------------
|
||||
*
|
||||
* @param func_argX
|
||||
* @param start
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
private static float noiseMapGetGradientCubicPoly(int func_argX, float start, float end) {
|
||||
float graph_gradient =
|
||||
-FastMath.pow(FastMath.pow(1 - terrainHeightFromZeroPoint, 3), -1) // 1/4 -> 3/4 -> 9/16 -> 16/9
|
||||
* (start - end) / FastMath.pow(height, 3)
|
||||
* FastMath.pow(func_argX - height, 3)
|
||||
+ end
|
||||
;
|
||||
|
||||
if (func_argX < terrainHeightFromZeroPoint) {
|
||||
return start;
|
||||
}
|
||||
else if (func_argX >= height) {
|
||||
return end;
|
||||
}
|
||||
else {
|
||||
return graph_gradient;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quadratic polynomial
|
||||
* -(16/9) * (start-end)/height^2 * (x - 0.25 * height)^2 + start
|
||||
* 16/9: terrain is formed from 1/4 of height.
|
||||
* 1 - (1/4) = 3/4, reverse it and square it.
|
||||
* That makes 16/9.
|
||||
*
|
||||
* Shape:
|
||||
*
|
||||
* cavity _
|
||||
* small
|
||||
* _
|
||||
* _
|
||||
* __
|
||||
* ____
|
||||
* cavity ________
|
||||
* large ________________
|
||||
*
|
||||
* @param func_argX
|
||||
* @param start
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
private static float noiseMapGetGradientMinusQuadPoly(int func_argX, float start, float end) {
|
||||
float graph_gradient =
|
||||
-FastMath.pow(FastMath.sqr(1 - terrainHeightFromZeroPoint), -1) // 1/4 -> 3/4 -> 9/16 -> 16/9
|
||||
* (start - end) / FastMath.sqr(height)
|
||||
* FastMath.sqr(func_argX - terrainHeightFromZeroPoint)
|
||||
+ start
|
||||
;
|
||||
|
||||
if (func_argX < terrainHeightFromZeroPoint) {
|
||||
return start;
|
||||
}
|
||||
else if (func_argX >= height) {
|
||||
return end;
|
||||
}
|
||||
else {
|
||||
return graph_gradient;
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateFloatingIslands() {
|
||||
System.out.println("[MapGenerator] Placing floating islands...");
|
||||
|
||||
int nIslands = map.width / 1024;
|
||||
int prevIndex = -1;
|
||||
|
||||
for (int i = 0; i < nIslands; i++) {
|
||||
int currentIndex = random.nextInt(FloatingIslandsPreset.presets);
|
||||
while (currentIndex == prevIndex) {
|
||||
currentIndex = random.nextInt(FloatingIslandsPreset.presets);
|
||||
}
|
||||
int[][] island = FloatingIslandsPreset.generatePreset(currentIndex, random);
|
||||
|
||||
int startingPosX = 1024 * i + 256 + random.nextInt(256);
|
||||
int startingPosY = minimumFloatingIsleHeight + random.nextInt(minimumFloatingIsleHeight);
|
||||
|
||||
if (random.nextFloat() >= floatingIslandsProb) {
|
||||
for (int j = 0; j < island.length; j++) {
|
||||
for (int k = 0; k < island[0].length; k++) {
|
||||
map.getTerrainArray()[j + startingPosY][k + startingPosX] = (byte) island[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Flood */
|
||||
|
||||
private static void floodBottomLava() {
|
||||
System.out.println("[MapGenerator] Flooding bottom lava...");
|
||||
for (int i = height * 14 / 15; i < height; i++) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
if (map.getTerrainArray()[i][j] == 0) {
|
||||
map.getTerrainArray()[i][j] = (byte) 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Plant */
|
||||
|
||||
private static void plantGrass() {
|
||||
System.out.println("[MapGenerator] Planting grass...");
|
||||
|
||||
int[] heightMap = new int[width]; //where topmost block sits
|
||||
|
||||
/* TODO composing dirt and stone
|
||||
* over certain level, use background dirt with stone 'peckles'
|
||||
* beetween levels, use background dirt with larger and denser stone peckles.
|
||||
* under another certain level, use background stone with dirt peckles.
|
||||
*/
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
//count down until hits block 2
|
||||
int grassCounter = 0;
|
||||
boolean hitDirt = false;
|
||||
|
||||
while (grassCounter < height - 2 && map.getTerrainArray()[grassCounter][i] == 0) {
|
||||
grassCounter++;
|
||||
}
|
||||
|
||||
//actually hit grass or just counting halted?
|
||||
if (map.getTerrainArray()[grassCounter][i] == 2) {
|
||||
hitDirt = true;
|
||||
}
|
||||
|
||||
//System.out.println(i+" ... "+grassCounter);
|
||||
|
||||
//plant grass
|
||||
if (hitDirt) {
|
||||
map.getTerrainArray()[grassCounter][i] = 3;
|
||||
}
|
||||
|
||||
//compose heightMap
|
||||
heightMap[i] = grassCounter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Post-process */
|
||||
|
||||
private static void fillOcean() {
|
||||
int oceanLeftHeight = 0;
|
||||
int oceanRightHeight = 0;
|
||||
|
||||
//get height
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
while (map.getTerrainArray()[oceanLeftHeight][OCEAN_WIDTH] == 0) {
|
||||
oceanLeftHeight++;
|
||||
}
|
||||
}
|
||||
else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
while (map.getTerrainArray()[oceanRightHeight][map.width - 1 - OCEAN_WIDTH] == 0) {
|
||||
oceanRightHeight++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < OCEAN_WIDTH * 1.5; i++) {
|
||||
int oceanDepthCounterLeft = 0;
|
||||
int oceanDepthCounterRight = 0;
|
||||
//flooding
|
||||
if (i < OCEAN_WIDTH) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
while (map.getTerrainArray()[oceanLeftHeight + oceanDepthCounterLeft][i] == 0) {
|
||||
map.getTerrainArray()[oceanLeftHeight + oceanDepthCounterLeft][i] = (byte) 239;
|
||||
oceanDepthCounterLeft++;
|
||||
}
|
||||
}
|
||||
else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
while (map.getTerrainArray()[oceanRightHeight + oceanDepthCounterRight][map.width - 1 - i] == 0) {
|
||||
map.getTerrainArray()[oceanRightHeight + oceanDepthCounterRight][map.width - 1 - i] = (byte) 239;
|
||||
oceanDepthCounterRight++;
|
||||
}
|
||||
}
|
||||
}
|
||||
//sand
|
||||
for (int j = 0; j < 40 - (i * 40 / (OCEAN_WIDTH + 20)); j++) { //20 => seashore size
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
map.getTerrainArray()[oceanLeftHeight + oceanDepthCounterLeft + j][i] = 14;
|
||||
}
|
||||
else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
map.getTerrainArray()[oceanRightHeight + oceanDepthCounterRight + j][map.width - 1 - i] = SAND;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Utility */
|
||||
|
||||
private static int clampN(int clampNumber, int num) {
|
||||
return FastMath.floor(num / clampNumber) * clampNumber;
|
||||
}
|
||||
|
||||
private static boolean outOfBound(int w, int h, int x, int y) {
|
||||
return !(x > 0 && y > 0 && x < w && y < h);
|
||||
}
|
||||
|
||||
private static float getDistance(float x1, float y1, float x2, float y2) {
|
||||
return FastMath.sqrt(FastMath.pow(x1 - x2, 2) + FastMath.pow(y2 - y1, 2));
|
||||
}
|
||||
|
||||
private static void circularDig(int i, int j, int brushSize, int fillFrom, int fill) {
|
||||
float halfBrushSize = (brushSize * 0.5f);
|
||||
|
||||
for (int pointerY = 0; pointerY < brushSize; pointerY++) {
|
||||
for (int pointerX = 0; pointerX < brushSize; pointerX++) {
|
||||
if (getDistance(j
|
||||
, i
|
||||
, j + pointerX - halfBrushSize
|
||||
, i + pointerY - halfBrushSize)
|
||||
<= FastMath.floor(brushSize / 2) - 1
|
||||
) {
|
||||
if (
|
||||
Math.round(j + pointerX - halfBrushSize) > brushSize
|
||||
&& Math.round(j + pointerX - halfBrushSize) < width - brushSize
|
||||
&& Math.round(i + pointerY - halfBrushSize) > brushSize
|
||||
&& Math.round(i + pointerY - halfBrushSize) < height - brushSize
|
||||
) {
|
||||
if (
|
||||
map.getTerrainArray()
|
||||
[Math.round(i + pointerY - halfBrushSize)]
|
||||
[Math.round(j + pointerX - halfBrushSize)]
|
||||
== (byte) fillFrom
|
||||
) {
|
||||
map.getTerrainArray()
|
||||
[Math.round(i + pointerY - halfBrushSize)]
|
||||
[Math.round(j + pointerX - halfBrushSize)]
|
||||
= (byte) fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/com/Torvald/Terrarum/MapGenerator/SimplexNoise.java
Normal file
78
src/com/Torvald/Terrarum/MapGenerator/SimplexNoise.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package com.Torvald.Terrarum.MapGenerator;
|
||||
|
||||
import com.Torvald.Rand.HighQualityRandom;
|
||||
import com.jme3.math.FastMath;
|
||||
|
||||
public class SimplexNoise {
|
||||
|
||||
SimplexNoise_octave[] octaves;
|
||||
float[] frequencys;
|
||||
float[] amplitudes;
|
||||
|
||||
int largestFeature;
|
||||
float persistence;
|
||||
long seed;
|
||||
|
||||
/**
|
||||
* @param largestFeature
|
||||
* @param persistence higher the value, rougher the output
|
||||
* @param seed
|
||||
*/
|
||||
public SimplexNoise(int largestFeature, float persistence, long seed) {
|
||||
this.largestFeature = largestFeature;
|
||||
this.persistence = persistence;
|
||||
this.seed = seed;
|
||||
|
||||
//receives a number (e.g. 128) and calculates what power of 2 it is (e.g. 2^7)
|
||||
int numberOfOctaves = FastMath.intLog2(largestFeature);
|
||||
|
||||
octaves = new SimplexNoise_octave[numberOfOctaves];
|
||||
frequencys = new float[numberOfOctaves];
|
||||
amplitudes = new float[numberOfOctaves];
|
||||
|
||||
HighQualityRandom rnd = new HighQualityRandom(seed);
|
||||
|
||||
for (int i = 0; i < numberOfOctaves; i++) {
|
||||
octaves[i] = new SimplexNoise_octave(rnd.nextInt());
|
||||
|
||||
frequencys[i] = FastMath.pow(2, i);
|
||||
amplitudes[i] = FastMath.pow(persistence, octaves.length - i);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public float getNoise(int x, int y) {
|
||||
|
||||
float result = 0;
|
||||
|
||||
for (int i = 0; i < octaves.length; i++) {
|
||||
//float frequency = FastMath.pow(2,i);
|
||||
//float amplitude = FastMath.pow(persistence,octaves.length-i);
|
||||
|
||||
result = result + (float) (octaves[i].noise(x / frequencys[i], y / frequencys[i]) * amplitudes[i]);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public float getNoise(int x, int y, int z) {
|
||||
|
||||
float result = 0;
|
||||
|
||||
for (int i = 0; i < octaves.length; i++) {
|
||||
float frequency = FastMath.pow(2, i);
|
||||
float amplitude = FastMath.pow(persistence, octaves.length - i);
|
||||
|
||||
result = result + (float) (octaves[i].noise(x / frequency, y / frequency, z / frequency) * amplitude);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
388
src/com/Torvald/Terrarum/MapGenerator/SimplexNoise_octave.java
Normal file
388
src/com/Torvald/Terrarum/MapGenerator/SimplexNoise_octave.java
Normal file
@@ -0,0 +1,388 @@
|
||||
package com.Torvald.Terrarum.MapGenerator;
|
||||
|
||||
/*
|
||||
* A speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.
|
||||
*
|
||||
* Based on example code by Stefan Gustavson (stegu@itn.liu.se).
|
||||
* Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
|
||||
* Better rank ordering method by Stefan Gustavson in 2012.
|
||||
*
|
||||
* This could be speeded up even further, but it's useful as it is.
|
||||
*
|
||||
* Version 2012-03-09
|
||||
*
|
||||
* This code was placed in the public domain by its original author,
|
||||
* Stefan Gustavson. You may use it as you see fit, but
|
||||
* attribution is appreciated.
|
||||
*
|
||||
*/
|
||||
|
||||
import com.Torvald.Rand.HighQualityRandom;
|
||||
|
||||
public class SimplexNoise_octave { // Simplex noise in 2D, 3D and 4D
|
||||
|
||||
public static int RANDOMSEED=0;
|
||||
private static int NUMBEROFSWAPS=400;
|
||||
|
||||
private static Grad grad3[] = {new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0),
|
||||
new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1),
|
||||
new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1)};
|
||||
|
||||
private static Grad grad4[]= {new Grad(0,1,1,1),new Grad(0,1,1,-1),new Grad(0,1,-1,1),new Grad(0,1,-1,-1),
|
||||
new Grad(0,-1,1,1),new Grad(0,-1,1,-1),new Grad(0,-1,-1,1),new Grad(0,-1,-1,-1),
|
||||
new Grad(1,0,1,1),new Grad(1,0,1,-1),new Grad(1,0,-1,1),new Grad(1,0,-1,-1),
|
||||
new Grad(-1,0,1,1),new Grad(-1,0,1,-1),new Grad(-1,0,-1,1),new Grad(-1,0,-1,-1),
|
||||
new Grad(1,1,0,1),new Grad(1,1,0,-1),new Grad(1,-1,0,1),new Grad(1,-1,0,-1),
|
||||
new Grad(-1,1,0,1),new Grad(-1,1,0,-1),new Grad(-1,-1,0,1),new Grad(-1,-1,0,-1),
|
||||
new Grad(1,1,1,0),new Grad(1,1,-1,0),new Grad(1,-1,1,0),new Grad(1,-1,-1,0),
|
||||
new Grad(-1,1,1,0),new Grad(-1,1,-1,0),new Grad(-1,-1,1,0),new Grad(-1,-1,-1,0)};
|
||||
|
||||
private static short p_supply[] = {151,160,137,91,90,15, //this contains all the numbers between 0 and 255, these are put in a random order depending upon the seed
|
||||
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
||||
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
||||
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
|
||||
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
|
||||
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
|
||||
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
|
||||
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
|
||||
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
|
||||
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
||||
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
||||
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180};
|
||||
|
||||
private short p[]=new short[p_supply.length];
|
||||
|
||||
// To remove the need for index wrapping, double the permutation table length
|
||||
private short perm[] = new short[512];
|
||||
private short permMod12[] = new short[512];
|
||||
public SimplexNoise_octave(int seed) {
|
||||
p=p_supply.clone();
|
||||
|
||||
if (seed==RANDOMSEED){
|
||||
throw new IllegalArgumentException("Seed cannot be zero.");
|
||||
}
|
||||
|
||||
//the random for the swaps
|
||||
HighQualityRandom rand=new HighQualityRandom(seed);
|
||||
|
||||
//the seed determines the swaps that occur between the default order and the order we're actually going to use
|
||||
for(int i=0;i<NUMBEROFSWAPS;i++){
|
||||
int swapFrom=rand.nextInt(p.length);
|
||||
int swapTo=rand.nextInt(p.length);
|
||||
|
||||
short temp=p[swapFrom];
|
||||
p[swapFrom]=p[swapTo];
|
||||
p[swapTo]=temp;
|
||||
}
|
||||
|
||||
|
||||
for(int i=0; i<512; i++)
|
||||
{
|
||||
perm[i]=p[i & 255];
|
||||
permMod12[i] = (short)(perm[i] % 12);
|
||||
}
|
||||
}
|
||||
|
||||
// Skewing and unskewing factors for 2, 3, and 4 dimensions
|
||||
private static final double F2 = 0.5*(Math.sqrt(3.0)-1.0);
|
||||
private static final double G2 = (3.0-Math.sqrt(3.0))/6.0;
|
||||
private static final double F3 = 1.0/3.0;
|
||||
private static final double G3 = 1.0/6.0;
|
||||
private static final double F4 = (Math.sqrt(5.0)-1.0)/4.0;
|
||||
private static final double G4 = (5.0-Math.sqrt(5.0))/20.0;
|
||||
|
||||
// This method is a *lot* faster than using (int)Math.floor(x)
|
||||
private static int fastfloor(double x) {
|
||||
int xi = (int)x;
|
||||
return x<xi ? xi-1 : xi;
|
||||
}
|
||||
|
||||
private static double dot(Grad g, double x, double y) {
|
||||
return g.x*x + g.y*y; }
|
||||
|
||||
private static double dot(Grad g, double x, double y, double z) {
|
||||
return g.x*x + g.y*y + g.z*z; }
|
||||
|
||||
private static double dot(Grad g, double x, double y, double z, double w) {
|
||||
return g.x*x + g.y*y + g.z*z + g.w*w; }
|
||||
|
||||
|
||||
// 2D simplex noise
|
||||
public double noise(double xin, double yin) {
|
||||
double n0, n1, n2; // Noise contributions from the three corners
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
double s = (xin+yin)*F2; // Hairy factor for 2D
|
||||
int i = fastfloor(xin+s);
|
||||
int j = fastfloor(yin+s);
|
||||
double t = (i+j)*G2;
|
||||
double X0 = i-t; // Unskew the cell origin back to (x,y) space
|
||||
double Y0 = j-t;
|
||||
double x0 = xin-X0; // The x,y distances from the cell origin
|
||||
double y0 = yin-Y0;
|
||||
// For the 2D case, the simplex shape is an equilateral triangle.
|
||||
// Determine which simplex we are in.
|
||||
int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
|
||||
if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
|
||||
else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
|
||||
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
|
||||
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
|
||||
// c = (3-sqrt(3))/6
|
||||
double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
|
||||
double y1 = y0 - j1 + G2;
|
||||
double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
|
||||
double y2 = y0 - 1.0 + 2.0 * G2;
|
||||
// Work out the hashed gradient indices of the three simplex corners
|
||||
int ii = i & 255;
|
||||
int jj = j & 255;
|
||||
int gi0 = permMod12[ii+perm[jj]];
|
||||
int gi1 = permMod12[ii+i1+perm[jj+j1]];
|
||||
int gi2 = permMod12[ii+1+perm[jj+1]];
|
||||
// Calculate the contribution from the three corners
|
||||
double t0 = 0.5 - x0*x0-y0*y0;
|
||||
if(t0<0) n0 = 0.0;
|
||||
else {
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient
|
||||
}
|
||||
double t1 = 0.5 - x1*x1-y1*y1;
|
||||
if(t1<0) n1 = 0.0;
|
||||
else {
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
|
||||
}
|
||||
double t2 = 0.5 - x2*x2-y2*y2;
|
||||
if(t2<0) n2 = 0.0;
|
||||
else {
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * dot(grad3[gi2], x2, y2);
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to return values in the interval [-1,1].
|
||||
return 70.0 * (n0 + n1 + n2);
|
||||
}
|
||||
|
||||
|
||||
// 3D simplex noise
|
||||
public double noise(double xin, double yin, double zin) {
|
||||
double n0, n1, n2, n3; // Noise contributions from the four corners
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
double s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D
|
||||
int i = fastfloor(xin+s);
|
||||
int j = fastfloor(yin+s);
|
||||
int k = fastfloor(zin+s);
|
||||
double t = (i+j+k)*G3;
|
||||
double X0 = i-t; // Unskew the cell origin back to (x,y,z) space
|
||||
double Y0 = j-t;
|
||||
double Z0 = k-t;
|
||||
double x0 = xin-X0; // The x,y,z distances from the cell origin
|
||||
double y0 = yin-Y0;
|
||||
double z0 = zin-Z0;
|
||||
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
|
||||
// Determine which simplex we are in.
|
||||
int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
|
||||
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
|
||||
if(x0>=y0) {
|
||||
if(y0>=z0)
|
||||
{ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
|
||||
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
|
||||
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
|
||||
}
|
||||
else { // x0<y0
|
||||
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
|
||||
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
|
||||
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
|
||||
}
|
||||
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
|
||||
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
|
||||
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
|
||||
// c = 1/6.
|
||||
double x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
|
||||
double y1 = y0 - j1 + G3;
|
||||
double z1 = z0 - k1 + G3;
|
||||
double x2 = x0 - i2 + 2.0*G3; // Offsets for third corner in (x,y,z) coords
|
||||
double y2 = y0 - j2 + 2.0*G3;
|
||||
double z2 = z0 - k2 + 2.0*G3;
|
||||
double x3 = x0 - 1.0 + 3.0*G3; // Offsets for last corner in (x,y,z) coords
|
||||
double y3 = y0 - 1.0 + 3.0*G3;
|
||||
double z3 = z0 - 1.0 + 3.0*G3;
|
||||
// Work out the hashed gradient indices of the four simplex corners
|
||||
int ii = i & 255;
|
||||
int jj = j & 255;
|
||||
int kk = k & 255;
|
||||
int gi0 = permMod12[ii+perm[jj+perm[kk]]];
|
||||
int gi1 = permMod12[ii+i1+perm[jj+j1+perm[kk+k1]]];
|
||||
int gi2 = permMod12[ii+i2+perm[jj+j2+perm[kk+k2]]];
|
||||
int gi3 = permMod12[ii+1+perm[jj+1+perm[kk+1]]];
|
||||
// Calculate the contribution from the four corners
|
||||
double t0 = 0.6 - x0*x0 - y0*y0 - z0*z0;
|
||||
if(t0<0) n0 = 0.0;
|
||||
else {
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0);
|
||||
}
|
||||
double t1 = 0.6 - x1*x1 - y1*y1 - z1*z1;
|
||||
if(t1<0) n1 = 0.0;
|
||||
else {
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1);
|
||||
}
|
||||
double t2 = 0.6 - x2*x2 - y2*y2 - z2*z2;
|
||||
if(t2<0) n2 = 0.0;
|
||||
else {
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2);
|
||||
}
|
||||
double t3 = 0.6 - x3*x3 - y3*y3 - z3*z3;
|
||||
if(t3<0) n3 = 0.0;
|
||||
else {
|
||||
t3 *= t3;
|
||||
n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3);
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to stay just inside [-1,1]
|
||||
return 32.0*(n0 + n1 + n2 + n3);
|
||||
}
|
||||
|
||||
|
||||
// 4D simplex noise, better simplex rank ordering method 2012-03-09
|
||||
public double noise(double x, double y, double z, double w) {
|
||||
|
||||
double n0, n1, n2, n3, n4; // Noise contributions from the five corners
|
||||
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
|
||||
double s = (x + y + z + w) * F4; // Factor for 4D skewing
|
||||
int i = fastfloor(x + s);
|
||||
int j = fastfloor(y + s);
|
||||
int k = fastfloor(z + s);
|
||||
int l = fastfloor(w + s);
|
||||
double t = (i + j + k + l) * G4; // Factor for 4D unskewing
|
||||
double X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
|
||||
double Y0 = j - t;
|
||||
double Z0 = k - t;
|
||||
double W0 = l - t;
|
||||
double x0 = x - X0; // The x,y,z,w distances from the cell origin
|
||||
double y0 = y - Y0;
|
||||
double z0 = z - Z0;
|
||||
double w0 = w - W0;
|
||||
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
|
||||
// To find out which of the 24 possible simplices we're in, we need to
|
||||
// determine the magnitude ordering of x0, y0, z0 and w0.
|
||||
// Six pair-wise comparisons are performed between each possible pair
|
||||
// of the four coordinates, and the results are used to rank the numbers.
|
||||
int rankx = 0;
|
||||
int ranky = 0;
|
||||
int rankz = 0;
|
||||
int rankw = 0;
|
||||
if(x0 > y0) rankx++; else ranky++;
|
||||
if(x0 > z0) rankx++; else rankz++;
|
||||
if(x0 > w0) rankx++; else rankw++;
|
||||
if(y0 > z0) ranky++; else rankz++;
|
||||
if(y0 > w0) ranky++; else rankw++;
|
||||
if(z0 > w0) rankz++; else rankw++;
|
||||
int i1, j1, k1, l1; // The integer offsets for the second simplex corner
|
||||
int i2, j2, k2, l2; // The integer offsets for the third simplex corner
|
||||
int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
|
||||
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
|
||||
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
|
||||
// impossible. Only the 24 indices which have non-zero entries make any sense.
|
||||
// We use a thresholding to set the coordinates in turn from the largest magnitude.
|
||||
// Rank 3 denotes the largest coordinate.
|
||||
i1 = rankx >= 3 ? 1 : 0;
|
||||
j1 = ranky >= 3 ? 1 : 0;
|
||||
k1 = rankz >= 3 ? 1 : 0;
|
||||
l1 = rankw >= 3 ? 1 : 0;
|
||||
// Rank 2 denotes the second largest coordinate.
|
||||
i2 = rankx >= 2 ? 1 : 0;
|
||||
j2 = ranky >= 2 ? 1 : 0;
|
||||
k2 = rankz >= 2 ? 1 : 0;
|
||||
l2 = rankw >= 2 ? 1 : 0;
|
||||
// Rank 1 denotes the second smallest coordinate.
|
||||
i3 = rankx >= 1 ? 1 : 0;
|
||||
j3 = ranky >= 1 ? 1 : 0;
|
||||
k3 = rankz >= 1 ? 1 : 0;
|
||||
l3 = rankw >= 1 ? 1 : 0;
|
||||
// The fifth corner has all coordinate offsets = 1, so no need to compute that.
|
||||
double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
|
||||
double y1 = y0 - j1 + G4;
|
||||
double z1 = z0 - k1 + G4;
|
||||
double w1 = w0 - l1 + G4;
|
||||
double x2 = x0 - i2 + 2.0*G4; // Offsets for third corner in (x,y,z,w) coords
|
||||
double y2 = y0 - j2 + 2.0*G4;
|
||||
double z2 = z0 - k2 + 2.0*G4;
|
||||
double w2 = w0 - l2 + 2.0*G4;
|
||||
double x3 = x0 - i3 + 3.0*G4; // Offsets for fourth corner in (x,y,z,w) coords
|
||||
double y3 = y0 - j3 + 3.0*G4;
|
||||
double z3 = z0 - k3 + 3.0*G4;
|
||||
double w3 = w0 - l3 + 3.0*G4;
|
||||
double x4 = x0 - 1.0 + 4.0*G4; // Offsets for last corner in (x,y,z,w) coords
|
||||
double y4 = y0 - 1.0 + 4.0*G4;
|
||||
double z4 = z0 - 1.0 + 4.0*G4;
|
||||
double w4 = w0 - 1.0 + 4.0*G4;
|
||||
// Work out the hashed gradient indices of the five simplex corners
|
||||
int ii = i & 255;
|
||||
int jj = j & 255;
|
||||
int kk = k & 255;
|
||||
int ll = l & 255;
|
||||
int gi0 = perm[ii+perm[jj+perm[kk+perm[ll]]]] % 32;
|
||||
int gi1 = perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]] % 32;
|
||||
int gi2 = perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]] % 32;
|
||||
int gi3 = perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]] % 32;
|
||||
int gi4 = perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]] % 32;
|
||||
// Calculate the contribution from the five corners
|
||||
double t0 = 0.6 - x0*x0 - y0*y0 - z0*z0 - w0*w0;
|
||||
if(t0<0) n0 = 0.0;
|
||||
else {
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0);
|
||||
}
|
||||
double t1 = 0.6 - x1*x1 - y1*y1 - z1*z1 - w1*w1;
|
||||
if(t1<0) n1 = 0.0;
|
||||
else {
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1);
|
||||
}
|
||||
double t2 = 0.6 - x2*x2 - y2*y2 - z2*z2 - w2*w2;
|
||||
if(t2<0) n2 = 0.0;
|
||||
else {
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2);
|
||||
}
|
||||
double t3 = 0.6 - x3*x3 - y3*y3 - z3*z3 - w3*w3;
|
||||
if(t3<0) n3 = 0.0;
|
||||
else {
|
||||
t3 *= t3;
|
||||
n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3);
|
||||
}
|
||||
double t4 = 0.6 - x4*x4 - y4*y4 - z4*z4 - w4*w4;
|
||||
if(t4<0) n4 = 0.0;
|
||||
else {
|
||||
t4 *= t4;
|
||||
n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4);
|
||||
}
|
||||
// Sum up and scale the result to cover the range [-1,1]
|
||||
return 27.0 * (n0 + n1 + n2 + n3 + n4);
|
||||
}
|
||||
|
||||
// Inner class to speed upp gradient computations
|
||||
// (array access is a lot slower than member access)
|
||||
private static class Grad
|
||||
{
|
||||
double x, y, z, w;
|
||||
|
||||
Grad(double x, double y, double z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
Grad(double x, double y, double z, double w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset01.png
Normal file
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset02.png
Normal file
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset03.png
Normal file
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset03.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset04.png
Normal file
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset04.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset05.png
Normal file
BIN
src/com/Torvald/Terrarum/MapGenerator/floatingIslePreset05.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
207
src/com/Torvald/Terrarum/Terrarum.java
Normal file
207
src/com/Torvald/Terrarum/Terrarum.java
Normal file
@@ -0,0 +1,207 @@
|
||||
package com.Torvald.Terrarum;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.Torvald.ImageFont.GameFontBase;
|
||||
import com.Torvald.ImageFont.GameFontBlack;
|
||||
import com.Torvald.ImageFont.GameFontWhite;
|
||||
import com.Torvald.Terrarum.Actors.PlayerBuildFactory;
|
||||
import com.Torvald.Terrarum.GameControl.GameController;
|
||||
import com.Torvald.Terrarum.GameControl.KeyMap;
|
||||
import com.Torvald.Terrarum.LangPack.Lang;
|
||||
import org.newdawn.slick.*;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-30.
|
||||
*/
|
||||
public class Terrarum extends BasicGame {
|
||||
|
||||
public static AppGameContainer appgc;
|
||||
public static final int WIDTH = 960;
|
||||
public static final int HEIGHT = 720;
|
||||
private static Game game;
|
||||
public static final int TARGET_FPS = 50;
|
||||
|
||||
/**
|
||||
* To be used with render, to achieve smooth frame drawing
|
||||
*
|
||||
* TARGET_INTERNAL_FPS > TARGET_FPS for smooth frame drawing
|
||||
*/
|
||||
public static final int TARGET_INTERNAL_FPS = 100;
|
||||
|
||||
public static String OSName;
|
||||
public static String OSVersion;
|
||||
public static String OperationSystem;
|
||||
public static String defaultDir;
|
||||
public static String defaultSaveDir;
|
||||
|
||||
public static String gameLocale = "fr";
|
||||
|
||||
public static Font gameFontWhite;
|
||||
|
||||
public static long memInUse;
|
||||
public static long totalVMMem;
|
||||
|
||||
public Terrarum(String gamename) {
|
||||
super(gamename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer gc) throws SlickException {
|
||||
getDefaultDirectory();
|
||||
createDirs();
|
||||
try {
|
||||
createFiles();
|
||||
new Lang();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
game = new Game();
|
||||
|
||||
new GameController();
|
||||
KeyMap.build();
|
||||
GameController.setKeyMap(new KeyMap());
|
||||
|
||||
gameFontWhite = new GameFontWhite();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer gc, int delta_t) throws SlickException{
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
memInUse = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() >> 20;
|
||||
totalVMMem = runtime.maxMemory() >> 20;
|
||||
|
||||
appgc.setTitle(
|
||||
"Simple Slick Game — FPS: "
|
||||
+ appgc.getFPS() + " ("
|
||||
+ String.valueOf(TARGET_INTERNAL_FPS)
|
||||
+ ") — "
|
||||
+ String.valueOf(memInUse) + "M / "
|
||||
+ String.valueOf(totalVMMem) + "M"
|
||||
);
|
||||
Game.update(gc, delta_t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer gc, Graphics g) throws SlickException
|
||||
{
|
||||
Game.render(gc, g);
|
||||
}
|
||||
|
||||
public void keyPressed(int key, char c) {
|
||||
GameController.keyPressed(key, c);
|
||||
}
|
||||
|
||||
public void keyReleased(int key, char c) {
|
||||
GameController.keyReleased(key, c);
|
||||
}
|
||||
|
||||
public void mouseMoved(int oldx, int oldy, int newx, int newy) {
|
||||
GameController.mouseMoved(oldx, oldy, newx, newy);
|
||||
}
|
||||
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
GameController.mouseDragged(oldx, oldy, newx, newy);
|
||||
}
|
||||
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
GameController.mousePressed(button, x, y);
|
||||
}
|
||||
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
GameController.mouseReleased(button, x, y);
|
||||
}
|
||||
|
||||
public void mouseWheelMoved(int change) {
|
||||
GameController.mouseWheelMoved(change);
|
||||
}
|
||||
|
||||
public void controllerButtonPressed(int controller, int button) {
|
||||
GameController.controllerButtonPressed(controller, button);
|
||||
}
|
||||
|
||||
public void controllerButtonReleased(int controller, int button) {
|
||||
GameController.controllerButtonReleased(controller, button);
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
appgc = new AppGameContainer(new Terrarum("Terrarum"));
|
||||
appgc.setDisplayMode(WIDTH, HEIGHT, false);
|
||||
appgc.setTargetFrameRate(TARGET_INTERNAL_FPS);
|
||||
appgc.setVSync(true);
|
||||
appgc.setShowFPS(false);
|
||||
appgc.setUpdateOnlyWhenVisible(false);
|
||||
appgc.setMaximumLogicUpdateInterval(1000 / TARGET_INTERNAL_FPS);
|
||||
appgc.start();
|
||||
}
|
||||
catch (SlickException ex)
|
||||
{
|
||||
Logger.getLogger(Terrarum.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void getDefaultDirectory(){
|
||||
OSName = System.getProperty("os.name");
|
||||
OSVersion = System.getProperty("os.version");
|
||||
|
||||
String OS = System.getProperty("os.name").toUpperCase();
|
||||
if (OS.contains("WIN")){
|
||||
OperationSystem = "WINDOWS";
|
||||
defaultDir = System.getenv("APPDATA") + "/Terrarum";
|
||||
}
|
||||
else if (OS.contains("OS X")){
|
||||
OperationSystem = "OSX";
|
||||
defaultDir = System.getProperty("user.home") + "/Library/Application "
|
||||
+ "Support" + "/Terrarum";
|
||||
}
|
||||
else if (OS.contains("NUX") || OS.contains("NIX")){
|
||||
OperationSystem = "LINUX";
|
||||
defaultDir = System.getProperty("user.home") + "/.terrarum";
|
||||
}
|
||||
else if (OS.contains("SUNOS")){
|
||||
OperationSystem = "SOLARIS";
|
||||
defaultDir = System.getProperty("user.home") + "/.terrarum";
|
||||
}
|
||||
else{
|
||||
OperationSystem = "UNKNOWN";
|
||||
defaultDir = System.getProperty("user.home") + "/.terrarum";
|
||||
}
|
||||
|
||||
defaultSaveDir = defaultDir + "/Saves";
|
||||
}
|
||||
|
||||
private static void createDirs(){
|
||||
File[] dirs = {
|
||||
new File(defaultSaveDir),
|
||||
};
|
||||
|
||||
for (File d : dirs){
|
||||
if (!d.exists()){
|
||||
d.mkdirs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createFiles() throws IOException {
|
||||
File[] files = {
|
||||
new File(defaultDir + "/properties")
|
||||
};
|
||||
|
||||
for (File f : files){
|
||||
if (!f.exists()){
|
||||
f.createNewFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/com/Torvald/Terrarum/TileProperties/propdata.csv
Normal file
19
src/com/Torvald/Terrarum/TileProperties/propdata.csv
Normal file
@@ -0,0 +1,19 @@
|
||||
"type: excel";
|
||||
"id";"name" ;"opacity";"strength";"isFluid";"viscosity";"isSolid";"isWall";"luminance";"drops" ;
|
||||
"0";"TILE_AIR" ; "0"; "0"; "0"; "0"; "0"; "0"; "0";"NULL" ;
|
||||
"1";"TILE_STONE" ; "8"; "25"; "0"; "0"; "1"; "1"; "0";"TILE_STONE" ;
|
||||
"2";"TILE_DIRT" ; "8"; "6"; "0"; "0"; "1"; "1"; "0";"TILE_DIRT" ;
|
||||
"3";"TILE_GRASS" ; "8"; "6"; "0"; "0"; "1"; "1"; "0";"TILE_DIRT" ;
|
||||
"4";"TILE_PLANK_NORMAL" ; "8"; "12"; "0"; "0"; "1"; "1"; "0";"TILE_PLANK_NORMAL" ;
|
||||
"5";"TILE_PLANK_EBONY" ; "8"; "12"; "0"; "0"; "1"; "1"; "0";"TILE_PLANK_EBONY" ;
|
||||
"6";"TILE_PLANK_BIRCH" ; "8"; "12"; "0"; "0"; "1"; "1"; "0";"TILE_PLANK_BIRCH" ;
|
||||
"7";"TILE_PLANK_ROSEWOOD" ; "8"; "12"; "0"; "0"; "1"; "1"; "0";"TILE_PLANK_ROSEWOOD" ;
|
||||
"8";"TILE_TRUNK_NORMAL" ; "8"; "12"; "0"; "0"; "1"; "0"; "0";"TILE_PLANK_NORMAL" ;
|
||||
"9";"TILE_TRUNK_EBONY" ; "8"; "12"; "0"; "0"; "1"; "0"; "0";"TILE_PLANK_EBONY" ;
|
||||
"10";"TILE_TRUNK_BIRCH" ; "8"; "12"; "0"; "0"; "1"; "0"; "0";"TILE_PLANK_BIRCH" ;
|
||||
"11";"TILE_TRUNK_ROSEWOOD" ; "8"; "12"; "0"; "0"; "1"; "0"; "0";"TILE_PLANK_ROSEWOOD" ;
|
||||
|
||||
"32";"TILE_SNOW" ; "8"; "6"; "0"; "0"; "1"; "1"; "0";"TILE_SNOW" ;
|
||||
"33";"TILE_ICE_FRAGILE" ; "1"; "1"; "0"; "0"; "1"; "0"; "0";"NULL" ;
|
||||
"34";"TILE_ICE_NATURAL" ; "6"; "25"; "0"; "0"; "1"; "1"; "0";"TILE_ICE_NATURAL" ;
|
||||
"35";"TILE_ICE_CLEAR_MAGICAL"; "8"; "25"; "0"; "0"; "1"; "1"; "1";"TILE_ICE_CLEAR_MAGICAL";
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 12.
|
22
src/com/Torvald/Terrarum/TileProperties/propdata.json
Normal file
22
src/com/Torvald/Terrarum/TileProperties/propdata.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"0": {
|
||||
"name": "TILE_AIR",
|
||||
"opacity": 0,
|
||||
"strength": 0,
|
||||
"isFluid": 0,
|
||||
"fluidViscocity": 0,
|
||||
"drop": "__null",
|
||||
"isSolid": 0,
|
||||
"isAlsoWall": 0
|
||||
},
|
||||
"1": {
|
||||
"name": "TILE_STONE",
|
||||
"opacity": 8,
|
||||
"strength": 25,
|
||||
"isFluid": 0,
|
||||
"fluidViscocity": 0,
|
||||
"drop": "item.stone",
|
||||
"isSolid": 0,
|
||||
"isAlsoWall": 0
|
||||
}
|
||||
}
|
||||
42
src/com/Torvald/Terrarum/TileStat/TileStat.java
Normal file
42
src/com/Torvald/Terrarum/TileStat/TileStat.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.Torvald.Terrarum.TileStat;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.GameMap.MapLayer;
|
||||
import com.Torvald.Terrarum.MapDrawer.MapCamera;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-01.
|
||||
*/
|
||||
public class TileStat {
|
||||
|
||||
private static short[] tilestat = new short[MapLayer.TILES_SUPPORTED];
|
||||
|
||||
public static void update() {
|
||||
Arrays.fill(tilestat, (short) 0);
|
||||
|
||||
int for_x_start = MapCamera.getRenderStartX();
|
||||
int for_y_start = MapCamera.getRenderStartY();
|
||||
int for_x_end = MapCamera.getRenderEndX();
|
||||
int for_y_end = MapCamera.getRenderEndY();
|
||||
|
||||
for (int y = for_y_start; y < for_y_end; y++) {
|
||||
for (int x = for_x_start; x < for_x_end; x++) {
|
||||
int tileWall = Game.map.getTileFromWall(x, y);
|
||||
int tileTerrain = Game.map.getTileFromTerrain(x, y);
|
||||
tilestat[tileWall] += 1;
|
||||
tilestat[tileTerrain] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int getCount(int... tile) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < tile.length; i++) {
|
||||
sum += Short.toUnsignedInt(tilestat[tile[i]]);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
}
|
||||
237
src/com/Torvald/Terrarum/UserInterface/BasicDebugInfoWindow.java
Normal file
237
src/com/Torvald/Terrarum/UserInterface/BasicDebugInfoWindow.java
Normal file
@@ -0,0 +1,237 @@
|
||||
package com.Torvald.Terrarum.UserInterface;
|
||||
|
||||
import com.Torvald.Terrarum.Actors.PlayerDebugger;
|
||||
import com.Torvald.Terrarum.Actors.Hitbox;
|
||||
import com.Torvald.Terrarum.LangPack.Lang;
|
||||
import com.Torvald.Terrarum.MapDrawer.LightmapRenderer;
|
||||
import com.Torvald.Terrarum.MapDrawer.MapDrawer;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.MapDrawer.MapCamera;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
|
||||
import java.util.Formatter;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-19.
|
||||
*/
|
||||
public class BasicDebugInfoWindow implements UICanvas {
|
||||
|
||||
private static PlayerDebugger playerDbg;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/**
|
||||
* Call AFTER player constuction!
|
||||
*/
|
||||
public BasicDebugInfoWindow() {
|
||||
width = Terrarum.WIDTH;
|
||||
height = Terrarum.HEIGHT;
|
||||
|
||||
playerDbg = new PlayerDebugger(Game.getPlayer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer gc, Graphics g) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Formatter formatter = new Formatter(sb);
|
||||
|
||||
int mouseTileX = (int) ((MapCamera.getCameraX() + gc.getInput().getMouseX() / Game.screenZoom)
|
||||
/ MapDrawer.TILE_SIZE);
|
||||
int mouseTileY = (int) ((MapCamera.getCameraY() + gc.getInput().getMouseY() / Game.screenZoom)
|
||||
/ MapDrawer.TILE_SIZE);
|
||||
|
||||
g.setColor(Color.white);
|
||||
|
||||
Hitbox hitbox = playerDbg.hitbox();
|
||||
Hitbox nextHitbox = playerDbg.nextHitbox();
|
||||
|
||||
printLine(g, 1, "posX : "
|
||||
+ String.valueOf(hitbox.getPointedX())
|
||||
+ " ("
|
||||
+ String.valueOf((int) (hitbox.getPointedX() / MapDrawer.TILE_SIZE))
|
||||
+ ")"
|
||||
);
|
||||
printLine(g, 2, "posY : "
|
||||
+ String.valueOf(hitbox.getPointedY())
|
||||
+ " ("
|
||||
+ String.valueOf((int) (hitbox.getPointedY() / MapDrawer.TILE_SIZE))
|
||||
+ ")"
|
||||
);
|
||||
printLine(g, 3, "veloX : " + String.valueOf(playerDbg.veloX()));
|
||||
printLine(g, 4, "veloY : " + String.valueOf(playerDbg.veloY()));
|
||||
printLine(g, 5, "grounded : " + String.valueOf(playerDbg.grounded()));
|
||||
printLine(g, 6, "noClip : " + String.valueOf(playerDbg.noClip()));
|
||||
printLine(g, 7, Lang.get("DBG_TOTAL_ACCEL_X") + " : " + String.valueOf(
|
||||
(float) playerDbg.actorValue().get("accel")
|
||||
* (float) playerDbg.actorValue().get("accelmult")
|
||||
)
|
||||
+ " (" + String.valueOf(playerDbg.getPlayer().readonly_totalX) + ")");
|
||||
printLine(g, 8
|
||||
, Lang.get("TERM_PHYS_MASS")
|
||||
+ " : "
|
||||
+ String.valueOf(playerDbg.mass())
|
||||
+ " [kg]"
|
||||
);
|
||||
|
||||
String lightVal;
|
||||
try {
|
||||
lightVal = Integer.toHexString(LightmapRenderer.getValueFromMap(mouseTileX, mouseTileY)).toUpperCase();
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {
|
||||
lightVal = "out of bounds";
|
||||
}
|
||||
printLine(g, 9, "light at cursor : " + lightVal);
|
||||
|
||||
String tileNo;
|
||||
try {
|
||||
tileNo = String.valueOf(Game.map.getTileFromTerrain(mouseTileX, mouseTileY));
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {
|
||||
tileNo = "out of bounds";
|
||||
}
|
||||
printLine(g, 10, "tile : " + tileNo);
|
||||
|
||||
// Memory allocation
|
||||
long memInUse = Terrarum.memInUse;
|
||||
long totalVMMem = Terrarum.totalVMMem;
|
||||
|
||||
g.setColor(new Color(0xFF7F00));
|
||||
g.drawString(
|
||||
Lang.get("DEV_MEMORY_SHORT_CAP")
|
||||
+ " : "
|
||||
+ formatter.format(
|
||||
Lang.get("DEV_MEMORY_A_OF_B")
|
||||
, memInUse
|
||||
, totalVMMem
|
||||
)
|
||||
, Terrarum.WIDTH - 200, line(1)
|
||||
);
|
||||
|
||||
// Hitbox
|
||||
g.setColor(new Color(0x007f00));
|
||||
g.drawRect(hitbox.getHitboxStart().getX()
|
||||
- MapCamera.getCameraX()
|
||||
, hitbox.getHitboxStart().getY()
|
||||
- MapCamera.getCameraY()
|
||||
, hitbox.getWidth()
|
||||
, hitbox.getHeight()
|
||||
);
|
||||
// ...and its point
|
||||
g.fillRect(
|
||||
hitbox.getPointedX() - 1
|
||||
- MapCamera.getCameraX()
|
||||
, hitbox.getPointedY() - 1
|
||||
- MapCamera.getCameraY()
|
||||
, 3
|
||||
, 3
|
||||
);
|
||||
g.drawString(
|
||||
Lang.get("DEV_COLOUR_LEGEND_GREEN")
|
||||
+ " : hitbox", Terrarum.WIDTH - 200
|
||||
, line(2)
|
||||
);
|
||||
|
||||
// Next hitbox
|
||||
g.setColor(Color.blue);
|
||||
g.drawRect(nextHitbox.getHitboxStart().getX()
|
||||
- MapCamera.getCameraX()
|
||||
, nextHitbox.getHitboxStart().getY()
|
||||
- MapCamera.getCameraY()
|
||||
, nextHitbox.getWidth()
|
||||
, nextHitbox.getHeight()
|
||||
);
|
||||
// ...and its point
|
||||
g.fillRect(
|
||||
nextHitbox.getPointedX() - 1
|
||||
- MapCamera.getCameraX()
|
||||
, nextHitbox.getPointedY() - 1
|
||||
- MapCamera.getCameraY()
|
||||
, 3
|
||||
, 3
|
||||
);
|
||||
g.drawString(
|
||||
Lang.get("DEV_COLOUR_LEGEND_BLUE")
|
||||
+ " : nextHitbox", Terrarum.WIDTH - 200
|
||||
, line(3)
|
||||
);
|
||||
}
|
||||
|
||||
private static void printLine(Graphics g, int l, String s) {
|
||||
g.drawString(s, 20, line(l));
|
||||
}
|
||||
|
||||
private static int line(int i) {
|
||||
return i * 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer gc, int delta_t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(int key, char c) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int change) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controllerButtonPressed(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controllerButtonReleased(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInput(Input input) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
}
|
||||
135
src/com/Torvald/Terrarum/UserInterface/Bulletin.java
Normal file
135
src/com/Torvald/Terrarum/UserInterface/Bulletin.java
Normal file
@@ -0,0 +1,135 @@
|
||||
package com.Torvald.Terrarum.UserInterface;
|
||||
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
import org.newdawn.slick.geom.RoundedRectangle;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-23.
|
||||
*/
|
||||
public class Bulletin implements UICanvas {
|
||||
|
||||
int width = 400;
|
||||
int height;
|
||||
|
||||
int visibleTime = 5000;
|
||||
int showupTimeConuter = 0;
|
||||
|
||||
boolean isShowing = false;
|
||||
String[] message;
|
||||
|
||||
Color uiColour = new Color(0x90000000);
|
||||
|
||||
final int FRAME_SIZE = 5;
|
||||
|
||||
private RoundedRectangle uiBox = new RoundedRectangle(
|
||||
width
|
||||
, Terrarum.HEIGHT
|
||||
, FRAME_SIZE
|
||||
, 2
|
||||
, RoundedRectangle.TOP_LEFT + RoundedRectangle.TOP_RIGHT
|
||||
);
|
||||
|
||||
@Override
|
||||
public void update(GameContainer gc, int delta_t) {
|
||||
if (showupTimeConuter >= visibleTime) {
|
||||
isShowing = false;
|
||||
}
|
||||
|
||||
if (isShowing) {
|
||||
showupTimeConuter += delta_t;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer gc, Graphics g) {
|
||||
/*if (isShowing) {
|
||||
int lineHeight = Terrarum.gameFontWhite.getLineHeight();
|
||||
|
||||
g.setColor(uiColour);
|
||||
g.fillRect(0
|
||||
, getHeight() - message.length * lineHeight
|
||||
- 10
|
||||
, width
|
||||
, getHeight()
|
||||
);
|
||||
|
||||
for (int i = 0; i < message.length; i++) {
|
||||
g.drawString(message[i]
|
||||
, 5
|
||||
, getHeight() - message.length * lineHeight
|
||||
+ 5
|
||||
+ (i * lineHeight)
|
||||
);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(int key, char c) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int change) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controllerButtonPressed(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controllerButtonReleased(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInput(Input input) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return Terrarum.HEIGHT;
|
||||
}
|
||||
|
||||
public void sendBulletin(String[] message) {
|
||||
isShowing = true;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
194
src/com/Torvald/Terrarum/UserInterface/ConsoleWindow.java
Normal file
194
src/com/Torvald/Terrarum/UserInterface/ConsoleWindow.java
Normal file
@@ -0,0 +1,194 @@
|
||||
package com.Torvald.Terrarum.UserInterface;
|
||||
|
||||
import com.Torvald.Terrarum.LangPack.Lang;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import com.Torvald.Terrarum.ConsoleCommand.CommandInterpreter;
|
||||
import com.Torvald.Terrarum.GameControl.Key;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public class ConsoleWindow implements UICanvas {
|
||||
|
||||
Color UIColour = new Color(0x90000000);
|
||||
|
||||
private StringBuffer commandInputPool = new StringBuffer();
|
||||
private String prevCommand = "";
|
||||
|
||||
private int inputCursorPos = 0;
|
||||
|
||||
private final int MESSAGES_MAX = 5000;
|
||||
private String[] messages = new String[MESSAGES_MAX];
|
||||
private int messageDisplayPos = 0;
|
||||
private int messagesCount = 0;
|
||||
|
||||
private final int LINE_HEIGHT = 20;
|
||||
private final int MESSAGES_DISPLAY_COUNT = 9;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
public ConsoleWindow() {
|
||||
super();
|
||||
|
||||
width = Terrarum.WIDTH;
|
||||
height = 200;
|
||||
|
||||
sendMessage(Lang.get("DEV_MESSAGE_CONSOLE_CODEX"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer gc, int delta_t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer gc, Graphics g) {
|
||||
g.setColor(UIColour);
|
||||
g.fillRect(0, 0, width, height);
|
||||
g.fillRect(0, 0, width, LINE_HEIGHT);
|
||||
|
||||
String input = commandInputPool.toString();
|
||||
int inputDrawWidth = g.getFont().getWidth(input);
|
||||
int inputDrawHeight = g.getFont().getLineHeight();
|
||||
|
||||
g.setColor(Color.white);
|
||||
g.drawString(input, 1, 0);
|
||||
g.fillRect(inputDrawWidth, 0, 2, inputDrawHeight);
|
||||
|
||||
for (int i = 0; i < MESSAGES_DISPLAY_COUNT; i++) {
|
||||
String message = messages[messageDisplayPos + i];
|
||||
if (message != null) {
|
||||
g.drawString(message, 1, LINE_HEIGHT * (i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void keyPressed(int key, char c) {
|
||||
// execute
|
||||
if (key == Key.RET && commandInputPool.length() > 0) {
|
||||
prevCommand = commandInputPool.toString();
|
||||
executeCommand();
|
||||
commandInputPool = new StringBuffer();
|
||||
}
|
||||
// backspace
|
||||
else if (key == Key.BKSP && commandInputPool.length() > 0) {
|
||||
commandInputPool.deleteCharAt(commandInputPool.length() - 1);
|
||||
}
|
||||
// get input
|
||||
else if ((key >= 2 && key <= 13)
|
||||
|| (key >= 16 && key <= 27)
|
||||
|| (key >= 30 && key <= 40)
|
||||
|| (key >= 44 && key <= 52)
|
||||
|| (commandInputPool.length() > 0 && key == 57)){
|
||||
commandInputPool.append(c);
|
||||
inputCursorPos += 1;
|
||||
}
|
||||
// prev command
|
||||
else if (key == Key.UP) {
|
||||
commandInputPool = new StringBuffer();
|
||||
commandInputPool.append(prevCommand);
|
||||
}
|
||||
// scroll up
|
||||
else if (key == Key.PGUP) {
|
||||
setDisplayPos(-MESSAGES_DISPLAY_COUNT + 1);
|
||||
}
|
||||
// scroll down
|
||||
else if (key == Key.PGDN) {
|
||||
setDisplayPos(MESSAGES_DISPLAY_COUNT - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(int key, char c) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int change) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controllerButtonPressed(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controllerButtonReleased(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInput(Input input) {
|
||||
|
||||
}
|
||||
|
||||
private void executeCommand() {
|
||||
sendMessage("> " + commandInputPool.toString());
|
||||
CommandInterpreter.execute(commandInputPool.toString());
|
||||
}
|
||||
|
||||
public void sendMessage(String msg) {
|
||||
messages[messagesCount] = new String(msg);
|
||||
messagesCount += 1;
|
||||
if (messagesCount > MESSAGES_DISPLAY_COUNT) {
|
||||
messageDisplayPos = messagesCount - MESSAGES_DISPLAY_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
private void setDisplayPos(int change) {
|
||||
int lowLim = 0;
|
||||
int highLim = (messagesCount <= MESSAGES_DISPLAY_COUNT)
|
||||
? 0
|
||||
: messagesCount - MESSAGES_DISPLAY_COUNT;
|
||||
int newVal = messageDisplayPos + change;
|
||||
|
||||
if (newVal < lowLim) {
|
||||
messageDisplayPos = lowLim;
|
||||
}
|
||||
else if (newVal > highLim) {
|
||||
messageDisplayPos = highLim;
|
||||
}
|
||||
else {
|
||||
messageDisplayPos = newVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
}
|
||||
132
src/com/Torvald/Terrarum/UserInterface/Message.java
Normal file
132
src/com/Torvald/Terrarum/UserInterface/Message.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package com.Torvald.Terrarum.UserInterface;
|
||||
|
||||
import com.Torvald.ImageFont.GameFontBlack;
|
||||
import com.Torvald.ImageFont.GameFontWhite;
|
||||
import org.newdawn.slick.*;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-27.
|
||||
*/
|
||||
public class Message implements UICanvas {
|
||||
|
||||
private Image segmentLeft, segmentRight, segmentBody;
|
||||
|
||||
private String[] messagesList;
|
||||
private int messagesShowingIndex = 0;
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
private int messageWindowRadius;
|
||||
|
||||
private Font uiFont;
|
||||
private final int GLYPH_HEIGHT = 20;
|
||||
|
||||
public Message(int width, boolean isBlackVariant) throws SlickException {
|
||||
if (!isBlackVariant) {
|
||||
segmentLeft = new Image("./res/graphics/gui/message_twoline_white_left.png");
|
||||
segmentRight = new Image("./res/graphics/gui/message_twoline_white_right.png");
|
||||
segmentBody = new Image("./res/graphics/gui/message_twoline_white_body.png");
|
||||
uiFont = new GameFontBlack();
|
||||
}
|
||||
else {
|
||||
segmentLeft = new Image("./res/graphics/gui/message_twoline_black_left.png");
|
||||
segmentRight = new Image("./res/graphics/gui/message_twoline_black_right.png");
|
||||
segmentBody = new Image("./res/graphics/gui/message_twoline_black_body.png");
|
||||
uiFont = new GameFontWhite();
|
||||
}
|
||||
|
||||
this.width = width;
|
||||
height = segmentLeft.getHeight();
|
||||
messageWindowRadius = segmentLeft.getWidth();
|
||||
}
|
||||
|
||||
public void setMessage(String[] messagesList) {
|
||||
this.messagesList = messagesList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer gc, int delta_t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer gc, Graphics g) {
|
||||
g.drawImage(segmentLeft, 0, 0);
|
||||
Image scaledSegCentre = segmentBody.getScaledCopy(
|
||||
width - (segmentRight.getWidth() + segmentLeft.getWidth())
|
||||
, segmentLeft.getHeight()
|
||||
);
|
||||
g.drawImage(scaledSegCentre, segmentLeft.getWidth(), 0);
|
||||
g.drawImage(segmentRight, width - segmentRight.getWidth(), 0);
|
||||
|
||||
g.setFont(uiFont);
|
||||
g.setDrawMode(Graphics.MODE_NORMAL);
|
||||
for (int i = messagesShowingIndex; i < messagesShowingIndex + 2; i++) {
|
||||
g.drawString(messagesList[i]
|
||||
, messageWindowRadius + 4
|
||||
, messageWindowRadius + (GLYPH_HEIGHT * (i - messagesShowingIndex))
|
||||
);
|
||||
}
|
||||
g.setDrawMode(Graphics.MODE_NORMAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int key, char c) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(int key, char c) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(int change) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controllerButtonPressed(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controllerButtonReleased(int controller, int button) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInput(Input input) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
}
|
||||
58
src/com/Torvald/Terrarum/UserInterface/UICanvas.java
Normal file
58
src/com/Torvald/Terrarum/UserInterface/UICanvas.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.Torvald.Terrarum.UserInterface;
|
||||
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Input;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31. <br>
|
||||
* <br>
|
||||
* Methods: <br>
|
||||
* UICanvas() <br>
|
||||
* update(GameContainer gc, int delta_t) <br>
|
||||
* render(GameContainer gc, Graphics g) <br>
|
||||
* keyPressed(int key, char c) <br>
|
||||
* keyReleased(int key, char c) <br>
|
||||
* mouseMoved(int oldx, int oldy, int newx, int newy) <br>
|
||||
* mouseDragged(int oldx, int oldy, int newx, int newy) <br>
|
||||
* mousePressed(int button, int x, int y) <br>
|
||||
* mouseReleased(int button, int x, int y) <br>
|
||||
* mouseWheelMoved(int change) <br>
|
||||
* controllerButtonPressed(int controller, int button) <br>
|
||||
* controllerButtonReleased(int controller, int button) <br>
|
||||
* processInput(Input input) <br>
|
||||
* <br>
|
||||
*/
|
||||
public interface UICanvas {
|
||||
|
||||
int getWidth();
|
||||
|
||||
int getHeight();
|
||||
|
||||
void update(GameContainer gc, int delta_t);
|
||||
|
||||
void render(GameContainer gc, Graphics g);
|
||||
|
||||
void keyPressed(int key, char c);
|
||||
|
||||
void keyReleased(int key, char c);
|
||||
|
||||
void mouseMoved(int oldx, int oldy, int newx, int newy);
|
||||
|
||||
void mouseDragged(int oldx, int oldy, int newx, int newy);
|
||||
|
||||
void mousePressed(int button, int x, int y);
|
||||
|
||||
void mouseReleased(int button, int x, int y);
|
||||
|
||||
void mouseWheelMoved(int change);
|
||||
|
||||
void controllerButtonPressed(int controller, int button);
|
||||
|
||||
void controllerButtonReleased(int controller, int button);
|
||||
|
||||
void processInput(Input input);
|
||||
|
||||
}
|
||||
191
src/com/Torvald/Terrarum/UserInterface/UIHandler.java
Normal file
191
src/com/Torvald/Terrarum/UserInterface/UIHandler.java
Normal file
@@ -0,0 +1,191 @@
|
||||
package com.Torvald.Terrarum.UserInterface;
|
||||
|
||||
import com.Torvald.Terrarum.MapDrawer.MapCamera;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import org.newdawn.slick.*;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public class UIHandler {
|
||||
|
||||
private UICanvas ui;
|
||||
|
||||
// X/Y Position to the game window.
|
||||
private int posX;
|
||||
private int posY;
|
||||
private boolean visible = false;
|
||||
|
||||
private boolean alwaysVisible = false;
|
||||
|
||||
private Graphics UIGraphicInstance;
|
||||
private Image UIDrawnCanvas;
|
||||
|
||||
private boolean opening = false;
|
||||
private boolean closing = false;
|
||||
|
||||
/**
|
||||
* Construct new UIHandler with given UI attached.
|
||||
* Invisible in default.
|
||||
* @param ui
|
||||
* @throws SlickException
|
||||
*/
|
||||
public UIHandler(UICanvas ui) throws SlickException{
|
||||
System.out.println(("[UIHandler] Creating UI '" + ui.getClass().getTypeName()) + "'");
|
||||
|
||||
this.ui = ui;
|
||||
|
||||
UIDrawnCanvas = new Image(
|
||||
FastMath.nearestPowerOfTwo(ui.getWidth())
|
||||
, FastMath.nearestPowerOfTwo(ui.getHeight())
|
||||
);
|
||||
|
||||
UIGraphicInstance = UIDrawnCanvas.getGraphics();
|
||||
|
||||
visible = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void update(GameContainer gc, int delta_t){
|
||||
if (visible || alwaysVisible) {
|
||||
ui.update(gc, delta_t);
|
||||
}
|
||||
}
|
||||
|
||||
public void render(GameContainer gc, Graphics gameGraphicInstance) {
|
||||
if (visible || alwaysVisible) {
|
||||
UIGraphicInstance.clear();
|
||||
UIGraphicInstance.setFont(Terrarum.gameFontWhite);
|
||||
|
||||
ui.render(gc, UIGraphicInstance);
|
||||
gameGraphicInstance.drawImage(UIDrawnCanvas
|
||||
// compensate for screenZoom AND camera translation
|
||||
// (see Game.render -> g.translate())
|
||||
, posX + MapCamera.getCameraX() * Game.screenZoom
|
||||
, posY + MapCamera.getCameraY() * Game.screenZoom
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPosition(int x, int y){
|
||||
posX = x;
|
||||
posY = y;
|
||||
}
|
||||
|
||||
public void setVisibility(boolean b){
|
||||
if (alwaysVisible) {
|
||||
throw new RuntimeException("Tried to 'set visibility of' constant UI");
|
||||
}
|
||||
visible = b;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getPosX() {
|
||||
return posX;
|
||||
}
|
||||
|
||||
public int getPosY() {
|
||||
return posY;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
if (alwaysVisible) {
|
||||
return true;
|
||||
} else {
|
||||
return visible;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAsAlwaysVisible() {
|
||||
alwaysVisible = true;
|
||||
visible = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setAsOpening(){
|
||||
if (alwaysVisible) {
|
||||
throw new RuntimeException("Tried to 'open' constant UI");
|
||||
}
|
||||
visible = true;
|
||||
}
|
||||
|
||||
public void setAsClosing(){
|
||||
if (alwaysVisible) {
|
||||
throw new RuntimeException("Tried to 'close' constant UI");
|
||||
}
|
||||
visible = false;
|
||||
}
|
||||
|
||||
public void toggleOpening() {
|
||||
if (alwaysVisible) {
|
||||
throw new RuntimeException("Tried to 'toggle opening of' constant UI");
|
||||
}
|
||||
if (visible) {
|
||||
if (!closing) {
|
||||
setAsClosing();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!opening) {
|
||||
setAsOpening();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void processInput(Input input) {
|
||||
if (visible) { ui.processInput(input); }
|
||||
}
|
||||
|
||||
public void keyPressed(int key, char c) {
|
||||
if (visible) { ui.keyPressed(key, c); }
|
||||
}
|
||||
|
||||
public void keyReleased(int key, char c) {
|
||||
if (visible) { ui.keyReleased(key, c); }
|
||||
}
|
||||
|
||||
public void mouseMoved(int oldx, int oldy, int newx, int newy) {
|
||||
if (visible) { ui.mouseMoved(oldx, oldy, newx, newy); }
|
||||
}
|
||||
|
||||
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
|
||||
if (visible) { ui.mouseDragged(oldx, oldy, newx, newy); }
|
||||
}
|
||||
|
||||
public void mousePressed(int button, int x, int y) {
|
||||
if (visible) { ui.mousePressed(button, x, y); }
|
||||
}
|
||||
|
||||
public void mouseReleased(int button, int x, int y) {
|
||||
if (visible) { ui.mouseReleased(button, x, y); }
|
||||
}
|
||||
|
||||
public void mouseWheelMoved(int change) {
|
||||
if (visible) { ui.mouseWheelMoved(change); }
|
||||
}
|
||||
|
||||
public void controllerButtonPressed(int controller, int button) {
|
||||
if (visible) { ui.controllerButtonPressed(controller, button); }
|
||||
}
|
||||
|
||||
public void controllerButtonReleased(int controller, int button) {
|
||||
if (visible) { ui.controllerButtonReleased(controller, button); }
|
||||
}
|
||||
|
||||
public boolean isTakingControl() {
|
||||
if (alwaysVisible) {
|
||||
return false; // constant UI can't take control
|
||||
}
|
||||
return (visible && !opening);
|
||||
}
|
||||
|
||||
public UICanvas getUI() {
|
||||
return ui;
|
||||
}
|
||||
}
|
||||
14
src/com/Torvald/Terrarum/UserInterface/UIItem.java
Normal file
14
src/com/Torvald/Terrarum/UserInterface/UIItem.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.Torvald.Terrarum.UserInterface;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public class UIItem {
|
||||
|
||||
// X/Y Position relative to the containing canvas
|
||||
int posX;
|
||||
int posY;
|
||||
|
||||
|
||||
|
||||
}
|
||||
141
src/com/Torvald/Terrarum/WorldTime.java
Normal file
141
src/com/Torvald/Terrarum/WorldTime.java
Normal file
@@ -0,0 +1,141 @@
|
||||
package com.Torvald.Terrarum;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-24.
|
||||
*/
|
||||
public class WorldTime {
|
||||
|
||||
public static int seconds = 0;
|
||||
public static int minutes = 0;
|
||||
public static int hours = 0;
|
||||
|
||||
public static int daysCount = 0; //NOT a calendar day
|
||||
|
||||
public static int days = 1;
|
||||
public static int months = 1;
|
||||
public static int years = 1;
|
||||
|
||||
public static int weeks = 1;
|
||||
public static int dayOfWeek = 0; //0: Mondag-The first day of weekday
|
||||
|
||||
public static final int DAY_LENGTH = 79200; //must be the multiple of 3600
|
||||
public static int timeDelta = 1;
|
||||
|
||||
public static final String[] DAYNAMES = { //daynames are taken from Nynorsk (å -> o)
|
||||
"Mondag"
|
||||
,"Tysdag"
|
||||
,"Midtedag" //From Islenska Miðvikudagur
|
||||
,"Torsdag"
|
||||
,"Fredag"
|
||||
,"Laurdag"
|
||||
,"Sundag"
|
||||
,"Verdag" //From Norsk word 'verd'
|
||||
};
|
||||
public static final String[] DAYNAMES_SHORT = {
|
||||
"Mon"
|
||||
,"Tys"
|
||||
,"Mid"
|
||||
,"Tor"
|
||||
,"Fre"
|
||||
,"Lau"
|
||||
,"Sun"
|
||||
,"Ver"
|
||||
};
|
||||
|
||||
/**
|
||||
* Note: Target FPS must be 60.
|
||||
*/
|
||||
public static void update(){
|
||||
//time
|
||||
seconds += timeDelta;
|
||||
|
||||
if (seconds >= 60){
|
||||
seconds = 0;
|
||||
minutes++;
|
||||
}
|
||||
|
||||
if (minutes >= 60){
|
||||
minutes = 0;
|
||||
hours++;
|
||||
}
|
||||
|
||||
if (hours >= DAY_LENGTH/3600){
|
||||
hours = 0;
|
||||
days++;
|
||||
daysCount++;
|
||||
dayOfWeek++;
|
||||
}
|
||||
|
||||
//calendar (the world calendar)
|
||||
if (dayOfWeek == 7){
|
||||
dayOfWeek = 0;
|
||||
}
|
||||
if ((months == 12 || (months == 7 && isLeapYear())) && days == 31){
|
||||
dayOfWeek = 7;
|
||||
}
|
||||
|
||||
if ((months == 12 || (months == 7 && isLeapYear())) && days == 32){
|
||||
days = 1;
|
||||
months = 1;
|
||||
years++;
|
||||
}
|
||||
else if ((months == 1 || months == 4 || months == 7 || months == 10) && days > 31){
|
||||
days = 0;
|
||||
months++;
|
||||
}
|
||||
else if (days > 30){
|
||||
days = 0;
|
||||
months++;
|
||||
}
|
||||
|
||||
if (months > 12){
|
||||
months = 1;
|
||||
years++;
|
||||
}
|
||||
}
|
||||
|
||||
public static int elapsedSeconds(){
|
||||
return (3600 * hours + 60 * minutes + seconds) % DAY_LENGTH;
|
||||
}
|
||||
|
||||
public static long totalSeconds(){
|
||||
return (long)(DAY_LENGTH) * daysCount + 3600 * hours + 60 * minutes + seconds;
|
||||
}
|
||||
|
||||
public static boolean isLeapYear(){
|
||||
boolean ret = false;
|
||||
|
||||
if (years % 4 == 0){
|
||||
ret = true;
|
||||
|
||||
if (years % 100 == 0){
|
||||
ret = false;
|
||||
|
||||
if (years % 400 == 0){
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void setTime(int t){
|
||||
days += t / DAY_LENGTH;
|
||||
hours = t / 3600;
|
||||
minutes = (t - 3600 * hours) / 60;
|
||||
seconds = t - minutes * 60;
|
||||
}
|
||||
|
||||
public static void addTime(int t){
|
||||
setTime(elapsedSeconds() + t);
|
||||
}
|
||||
|
||||
public static void setTimeDelta(int d){
|
||||
timeDelta = (d == 0) ? 1 : d;
|
||||
}
|
||||
|
||||
public static String getDayName(){
|
||||
return DAYNAMES[dayOfWeek];
|
||||
}
|
||||
}
|
||||
279
src/com/Torvald/spriteAnimation/SpriteAnimation.java
Normal file
279
src/com/Torvald/spriteAnimation/SpriteAnimation.java
Normal file
@@ -0,0 +1,279 @@
|
||||
|
||||
/* Original code author: Sean Laurvick
|
||||
* This code is based on the original author's code written in Lua.
|
||||
*/
|
||||
|
||||
package com.Torvald.spriteAnimation;
|
||||
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.SpriteSheet;
|
||||
|
||||
public class SpriteAnimation {
|
||||
|
||||
private SpriteSheet spriteImage;
|
||||
private Image[][] sprites;
|
||||
private int height;
|
||||
private int width;
|
||||
private int currentFrame = 1;
|
||||
private int currentRow = 1;
|
||||
private int nFrames;
|
||||
private int nRows;
|
||||
private int delay = 200;
|
||||
private int delta = 0;
|
||||
private boolean looping = true;
|
||||
private boolean animationRunning = true;
|
||||
private boolean flipHorizontal = false;
|
||||
private boolean flipVertical = false;
|
||||
private boolean visible = false;
|
||||
|
||||
private int offsetX = 0;
|
||||
private int offsetY = 0;
|
||||
|
||||
private float prevScale = 1f;
|
||||
private Image currentImage;
|
||||
|
||||
public SpriteAnimation() throws SlickException{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets spritesheet.
|
||||
* MUST be called AFTER setDimension.
|
||||
* @param imagePath path to the sprite sheet image.
|
||||
* @throws SlickException
|
||||
*/
|
||||
public void setSpriteImage(String imagePath) throws SlickException {
|
||||
spriteImage = new SpriteSheet(imagePath, this.width, this.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets animation delay. Will default to 200 if not called.
|
||||
* @param delay in milliseconds
|
||||
*/
|
||||
public void setDelay(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sprite dimension. This is necessary.
|
||||
* @param w
|
||||
* @param h
|
||||
*/
|
||||
public void setDimension(int w, int h) {
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sheet rows and animation frames. Will default to
|
||||
* 1, 1 (still image of top left from the sheet) if not called.
|
||||
* @param rows
|
||||
* @param frames
|
||||
*/
|
||||
public void setRowsAndFrames(int rows, int frames) {
|
||||
nRows = rows;
|
||||
nFrames = frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose (load from spritesheet) as attributes defined.
|
||||
* If attributes were not defined, will throw exception of
|
||||
* SlickException or ArraySizeException.
|
||||
* @throws SlickException
|
||||
*/
|
||||
public void composeSprite() throws SlickException {
|
||||
this.sprites = new Image[this.nRows][this.nFrames];
|
||||
|
||||
for (int i=0; i<this.nRows; i++){
|
||||
for (int j=0; j<this.nFrames; j++){
|
||||
this.sprites[i][j] = spriteImage.getSprite(j, i);
|
||||
this.sprites[i][j].setFilter(Image.FILTER_LINEAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setAsVisible() {
|
||||
visible = true;
|
||||
}
|
||||
|
||||
public void setAsInvisible() {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
public void update(int delta){
|
||||
if (animationRunning){//skip this if animation is stopped
|
||||
this.delta += delta;
|
||||
|
||||
//check if it's time to advance the frame
|
||||
if ( this.delta >= ( this.delay ) ){
|
||||
//if set to not loop, keep the frame at the last frame
|
||||
if ( this.currentFrame == this.nFrames && !(this.looping) ){
|
||||
this.currentFrame = this.nFrames - 1;
|
||||
}
|
||||
|
||||
//advance one frame, then reset delta counter
|
||||
this.currentFrame = (this.currentFrame % this.nFrames) + 1;
|
||||
this.delta = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render to specific coordinates. Will assume bottom-center point as image position.
|
||||
* Will round to integer.
|
||||
* @param g
|
||||
* @param posX bottom-center point
|
||||
* @param posY bottom-center point
|
||||
* @param scale
|
||||
*/
|
||||
public void render(Graphics g, float posX, float posY, float scale){
|
||||
scale *= Game.screenZoom;
|
||||
|
||||
// Null checking
|
||||
if (currentImage == null) {
|
||||
currentImage = getScaledSprite(scale);
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
// re-scale image if scale has been changed
|
||||
if (prevScale != scale) {
|
||||
currentImage = getScaledSprite(scale);
|
||||
prevScale = scale;
|
||||
}
|
||||
|
||||
Image flippedImage = currentImage.getFlippedCopy(flipHorizontal, flipVertical);
|
||||
|
||||
flippedImage.startUse();
|
||||
flippedImage.drawEmbedded(
|
||||
Math.round(posX * Game.screenZoom)
|
||||
, Math.round(posY * Game.screenZoom)
|
||||
, width * scale
|
||||
, height * scale
|
||||
);
|
||||
flippedImage.endUse();
|
||||
}
|
||||
}
|
||||
|
||||
public void render(Graphics g, float posX, float posY){
|
||||
render(g, posX, posY, 1);
|
||||
}
|
||||
|
||||
public void switchSprite(int newRow){
|
||||
currentRow = newRow;
|
||||
|
||||
//if beyond the frame index then reset
|
||||
if (currentFrame > nFrames){
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void switchSprite(int newRow, int newMax){
|
||||
if (newMax > 0){
|
||||
nFrames = newMax;
|
||||
}
|
||||
|
||||
currentRow = newRow;
|
||||
|
||||
//if beyond the frame index then reset
|
||||
if (currentFrame > nFrames){
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void switchSpriteDelay(int newDelay){
|
||||
if (newDelay > 0){
|
||||
delay = newDelay;
|
||||
}
|
||||
}
|
||||
|
||||
public void switchSprite(int newRow, int newMax, int newDelay){
|
||||
if (newMax > 0){
|
||||
nFrames = newMax;
|
||||
}
|
||||
|
||||
if (newDelay > 0){
|
||||
delay = newDelay;
|
||||
}
|
||||
|
||||
currentRow = newRow;
|
||||
|
||||
//if beyond the frame index then reset
|
||||
if (currentFrame > nFrames){
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
currentFrame = 1;
|
||||
}
|
||||
|
||||
public void start(){ //starts the animation
|
||||
animationRunning = true;
|
||||
}
|
||||
|
||||
public void start(int selectFrame){ //starts the animation
|
||||
animationRunning = true;
|
||||
|
||||
//optional: seleft the frame no which to start the animation
|
||||
currentFrame = selectFrame;
|
||||
}
|
||||
|
||||
public void stop(){
|
||||
animationRunning = false;
|
||||
}
|
||||
|
||||
public void stop(int selectFrame){
|
||||
animationRunning = false;
|
||||
|
||||
currentFrame = selectFrame;
|
||||
}
|
||||
|
||||
public void flip(boolean horizontal, boolean vertical){
|
||||
flipHorizontal = horizontal;
|
||||
flipVertical = vertical;
|
||||
}
|
||||
|
||||
public boolean flippedHorizontal() {
|
||||
return flipHorizontal;
|
||||
}
|
||||
|
||||
public boolean flippedVertical() {
|
||||
return flipVertical;
|
||||
}
|
||||
|
||||
public int getWidth(){
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight(){
|
||||
return height;
|
||||
}
|
||||
|
||||
private Image getScaledSprite(float scale) {
|
||||
Image selectedImage = sprites[currentRow - 1][currentFrame - 1];
|
||||
|
||||
// resample
|
||||
/*float nearestResampleScale = (scale > 1) ? Math.round(scale) : 1;
|
||||
float linearResampleScale = scale / nearestResampleScale;
|
||||
|
||||
// scale 1.8 -> resample in 2(nearest), then resample in 0.9(linear)
|
||||
// scale by nearestResampleScale (2, 3, ...)
|
||||
selectedImage.setFilter(Image.FILTER_NEAREST);
|
||||
Image selImgNearestScaled = selectedImage.getScaledCopy(nearestResampleScale);
|
||||
// scale by linearResampleScale (.x)
|
||||
Image selImgLinearScaled;
|
||||
if (scale % 1 > 0) {
|
||||
selImgNearestScaled.setFilter(Image.FILTER_LINEAR);
|
||||
selImgLinearScaled = selImgNearestScaled.getScaledCopy(linearResampleScale);
|
||||
return selImgLinearScaled;
|
||||
}
|
||||
else {
|
||||
return selImgNearestScaled;
|
||||
}*/
|
||||
selectedImage.setFilter(Image.FILTER_NEAREST);
|
||||
return selectedImage.getScaledCopy(scale);
|
||||
}
|
||||
}
|
||||
866
src/com/jme3/math/FastMath.java
Normal file
866
src/com/jme3/math/FastMath.java
Normal file
@@ -0,0 +1,866 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This source is a custom modification of the original code.
|
||||
*/
|
||||
|
||||
package com.jme3.math;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <code>FastMath</code> provides 'fast' math approximations and float equivalents of Math
|
||||
* functions. These are all used as static values and functions.
|
||||
*
|
||||
* @author Various
|
||||
* @version $Id: FastMath.java,v 1.45 2007/08/26 08:44:20 irrisor Exp $
|
||||
*/
|
||||
final public class FastMath {
|
||||
|
||||
private FastMath() {
|
||||
}
|
||||
/** A "close to zero" double epsilon value for use*/
|
||||
public static final double DBL_EPSILON = 2.220446049250313E-16d;
|
||||
/** A "close to zero" float epsilon value for use*/
|
||||
public static final float FLT_EPSILON = 1.1920928955078125E-7f;
|
||||
/** A "close to zero" float epsilon value for use*/
|
||||
public static final float ZERO_TOLERANCE = 0.0001f;
|
||||
public static final float ONE_THIRD = 1f / 3f;
|
||||
/** The value PI as a float. (180 degrees) */
|
||||
public static final float PI = (float) Math.PI;
|
||||
/** The value 2PI as a float. (360 degrees) */
|
||||
public static final float TWO_PI = 2.0f * PI;
|
||||
/** The value PI/2 as a float. (90 degrees) */
|
||||
public static final float HALF_PI = 0.5f * PI;
|
||||
/** The value PI/4 as a float. (45 degrees) */
|
||||
public static final float QUARTER_PI = 0.25f * PI;
|
||||
/** The value 1/PI as a float. */
|
||||
public static final float INV_PI = 1.0f / PI;
|
||||
/** The value 1/(2PI) as a float. */
|
||||
public static final float INV_TWO_PI = 1.0f / TWO_PI;
|
||||
/** A value to multiply a degree value by, to convert it to radians. */
|
||||
public static final float DEG_TO_RAD = PI / 180.0f;
|
||||
/** A value to multiply a radian value by, to convert it to degrees. */
|
||||
public static final float RAD_TO_DEG = 180.0f / PI;
|
||||
/** A precreated random object for random numbers. */
|
||||
public static final Random rand = new Random(System.currentTimeMillis());
|
||||
|
||||
/**
|
||||
* Returns true if the number is a power of 2 (2,4,8,16...)
|
||||
*
|
||||
* A good implementation found on the Java boards. note: a number is a power
|
||||
* of two if and only if it is the smallest number with that number of
|
||||
* significant bits. Therefore, if you subtract 1, you know that the new
|
||||
* number will have fewer bits, so ANDing the original number with anything
|
||||
* less than it will give 0.
|
||||
*
|
||||
* @param number
|
||||
* The number to test.
|
||||
* @return True if it is a power of two.
|
||||
*/
|
||||
public static boolean isPowerOfTwo(int number) {
|
||||
return (number > 0) && (number & (number - 1)) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next power of two of the given number.
|
||||
*
|
||||
* E.g. for an input 100, this returns 128.
|
||||
* Returns 1 for all numbers <= 1.
|
||||
*
|
||||
* @param number The number to obtain the POT for.
|
||||
* @return The next power of two.
|
||||
*/
|
||||
public static int nearestPowerOfTwo(int number) {
|
||||
number--;
|
||||
number |= number >> 1;
|
||||
number |= number >> 2;
|
||||
number |= number >> 4;
|
||||
number |= number >> 8;
|
||||
number |= number >> 16;
|
||||
number++;
|
||||
number += (number == 0) ? 1 : 0;
|
||||
return number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next binary log of the given number.
|
||||
*
|
||||
* E.g. for an input 100, this returns 6.
|
||||
* Throws runtimeException for all numbers <= 1.
|
||||
*
|
||||
* @param number The number to obtain the POT for.
|
||||
* @return The next power of two.
|
||||
*/
|
||||
public static int intLog2(int number) {
|
||||
if (number == 0) return 0;
|
||||
int log = 0;
|
||||
if( ( number & 0xffff0000 ) != 0 ) { number >>>= 16; log = 16; }
|
||||
if( number >= 256 ) { number >>>= 8; log += 8; }
|
||||
if( number >= 16 ) { number >>>= 4; log += 4; }
|
||||
if( number >= 4 ) { number >>>= 2; log += 2; }
|
||||
return log + ( number >>> 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear interpolation from startValue to endValue by the given percent.
|
||||
* Basically: ((1 - percent) * startValue) + (percent * endValue)
|
||||
*
|
||||
* @param scale
|
||||
* scale value to use. if 1, use endValue, if 0, use startValue.
|
||||
* @param startValue
|
||||
* Begining value. 0% of f
|
||||
* @param endValue
|
||||
* ending value. 100% of f
|
||||
* @return The interpolated value between startValue and endValue.
|
||||
*/
|
||||
public static float interpolateLinear(float scale, float startValue, float endValue) {
|
||||
if (startValue == endValue) {
|
||||
return startValue;
|
||||
}
|
||||
if (scale <= 0f) {
|
||||
return startValue;
|
||||
}
|
||||
if (scale >= 1f) {
|
||||
return endValue;
|
||||
}
|
||||
return ((1f - scale) * startValue) + (scale * endValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear interpolation from startValue to endValue by the given percent.
|
||||
* Basically: ((1 - percent) * startValue) + (percent * endValue)
|
||||
*
|
||||
* @param scale
|
||||
* scale value to use. if 1, use endValue, if 0, use startValue.
|
||||
* @param startValue
|
||||
* Begining value. 0% of f
|
||||
* @param endValue
|
||||
* ending value. 100% of f
|
||||
* @param store a vector3f to store the result
|
||||
* @return The interpolated value between startValue and endValue.
|
||||
*/
|
||||
public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store) {
|
||||
if (store == null) {
|
||||
store = new Vector3f();
|
||||
}
|
||||
store.x = interpolateLinear(scale, startValue.x, endValue.x);
|
||||
store.y = interpolateLinear(scale, startValue.y, endValue.y);
|
||||
store.z = interpolateLinear(scale, startValue.z, endValue.z);
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear interpolation from startValue to endValue by the given percent.
|
||||
* Basically: ((1 - percent) * startValue) + (percent * endValue)
|
||||
*
|
||||
* @param scale
|
||||
* scale value to use. if 1, use endValue, if 0, use startValue.
|
||||
* @param startValue
|
||||
* Begining value. 0% of f
|
||||
* @param endValue
|
||||
* ending value. 100% of f
|
||||
* @return The interpolated value between startValue and endValue.
|
||||
*/
|
||||
public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue) {
|
||||
return interpolateLinear(scale, startValue, endValue, null);
|
||||
}
|
||||
|
||||
/**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
|
||||
* here is the interpolation matrix
|
||||
* m = [ 0.0 1.0 0.0 0.0 ]
|
||||
* [-T 0.0 T 0.0 ]
|
||||
* [ 2T T-3 3-2T -T ]
|
||||
* [-T 2-T T-2 T ]
|
||||
* where T is the curve tension
|
||||
* the result is a value between p1 and p2, t=0 for p1, t=1 for p2
|
||||
* @param u value from 0 to 1
|
||||
* @param T The tension of the curve
|
||||
* @param p0 control point 0
|
||||
* @param p1 control point 1
|
||||
* @param p2 control point 2
|
||||
* @param p3 control point 3
|
||||
* @return catmull-Rom interpolation
|
||||
*/
|
||||
public static float interpolateCatmullRom(float u, float T, float p0, float p1, float p2, float p3) {
|
||||
double c1, c2, c3, c4;
|
||||
c1 = p1;
|
||||
c2 = -1.0 * T * p0 + T * p2;
|
||||
c3 = 2 * T * p0 + (T - 3) * p1 + (3 - 2 * T) * p2 + -T * p3;
|
||||
c4 = -T * p0 + (2 - T) * p1 + (T - 2) * p2 + T * p3;
|
||||
|
||||
return (float) (((c4 * u + c3) * u + c2) * u + c1);
|
||||
}
|
||||
|
||||
/**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
|
||||
* here is the interpolation matrix
|
||||
* m = [ 0.0 1.0 0.0 0.0 ]
|
||||
* [-T 0.0 T 0.0 ]
|
||||
* [ 2T T-3 3-2T -T ]
|
||||
* [-T 2-T T-2 T ]
|
||||
* where T is the tension of the curve
|
||||
* the result is a value between p1 and p2, t=0 for p1, t=1 for p2
|
||||
* @param u value from 0 to 1
|
||||
* @param T The tension of the curve
|
||||
* @param p0 control point 0
|
||||
* @param p1 control point 1
|
||||
* @param p2 control point 2
|
||||
* @param p3 control point 3
|
||||
* @param store a Vector3f to store the result
|
||||
* @return catmull-Rom interpolation
|
||||
*/
|
||||
public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store) {
|
||||
if (store == null) {
|
||||
store = new Vector3f();
|
||||
}
|
||||
store.x = interpolateCatmullRom(u, T, p0.x, p1.x, p2.x, p3.x);
|
||||
store.y = interpolateCatmullRom(u, T, p0.y, p1.y, p2.y, p3.y);
|
||||
store.z = interpolateCatmullRom(u, T, p0.z, p1.z, p2.z, p3.z);
|
||||
return store;
|
||||
}
|
||||
|
||||
/**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
|
||||
* here is the interpolation matrix
|
||||
* m = [ 0.0 1.0 0.0 0.0 ]
|
||||
* [-T 0.0 T 0.0 ]
|
||||
* [ 2T T-3 3-2T -T ]
|
||||
* [-T 2-T T-2 T ]
|
||||
* where T is the tension of the curve
|
||||
* the result is a value between p1 and p2, t=0 for p1, t=1 for p2
|
||||
* @param u value from 0 to 1
|
||||
* @param T The tension of the curve
|
||||
* @param p0 control point 0
|
||||
* @param p1 control point 1
|
||||
* @param p2 control point 2
|
||||
* @param p3 control point 3
|
||||
* @return catmull-Rom interpolation
|
||||
*/
|
||||
public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
|
||||
return interpolateCatmullRom(u, T, p0, p1, p2, p3, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the lenght on a catmull rom spline between control point 1 and 2
|
||||
* @param p0 control point 0
|
||||
* @param p1 control point 1
|
||||
* @param p2 control point 2
|
||||
* @param p3 control point 3
|
||||
* @param startRange the starting range on the segment (use 0)
|
||||
* @param endRange the end range on the segment (use 1)
|
||||
* @param curveTension the curve tension
|
||||
* @return the length of the segment
|
||||
*/
|
||||
public static float getCatmullRomP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, float startRange, float endRange, float curveTension) {
|
||||
|
||||
float epsilon = 0.001f;
|
||||
float middleValue = (startRange + endRange) * 0.5f;
|
||||
Vector3f start = p1.clone();
|
||||
if (startRange != 0) {
|
||||
FastMath.interpolateCatmullRom(startRange, curveTension, p0, p1, p2, p3, start);
|
||||
}
|
||||
Vector3f end = p2.clone();
|
||||
if (endRange != 1) {
|
||||
FastMath.interpolateCatmullRom(endRange, curveTension, p0, p1, p2, p3, end);
|
||||
}
|
||||
Vector3f middle = FastMath.interpolateCatmullRom(middleValue, curveTension, p0, p1, p2, p3);
|
||||
float l = end.subtract(start).length();
|
||||
float l1 = middle.subtract(start).length();
|
||||
float l2 = end.subtract(middle).length();
|
||||
float len = l1 + l2;
|
||||
if (l + epsilon < len) {
|
||||
l1 = getCatmullRomP1toP2Length(p0, p1, p2, p3, startRange, middleValue, curveTension);
|
||||
l2 = getCatmullRomP1toP2Length(p0, p1, p2, p3, middleValue, endRange, curveTension);
|
||||
}
|
||||
l = l1 + l2;
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the arc cosine of an angle given in radians.<br>
|
||||
* Special cases:
|
||||
* <ul><li>If fValue is smaller than -1, then the result is PI.
|
||||
* <li>If the argument is greater than 1, then the result is 0.</ul>
|
||||
* @param fValue The angle, in radians.
|
||||
* @return fValue's acos
|
||||
* @see java.lang.Math#acos(double)
|
||||
*/
|
||||
public static float acos(float fValue) {
|
||||
if (-1.0f < fValue) {
|
||||
if (fValue < 1.0f) {
|
||||
return (float) Math.acos(fValue);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return PI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arc sine of an angle given in radians.<br>
|
||||
* Special cases:
|
||||
* <ul><li>If fValue is smaller than -1, then the result is -HALF_PI.
|
||||
* <li>If the argument is greater than 1, then the result is HALF_PI.</ul>
|
||||
* @param fValue The angle, in radians.
|
||||
* @return fValue's asin
|
||||
* @see java.lang.Math#asin(double)
|
||||
*/
|
||||
public static float asin(float fValue) {
|
||||
if (-1.0f < fValue) {
|
||||
if (fValue < 1.0f) {
|
||||
return (float) Math.asin(fValue);
|
||||
}
|
||||
|
||||
return HALF_PI;
|
||||
}
|
||||
|
||||
return -HALF_PI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arc tangent of an angle given in radians.<br>
|
||||
* @param fValue The angle, in radians.
|
||||
* @return fValue's asin
|
||||
* @see java.lang.Math#atan(double)
|
||||
*/
|
||||
public static float atan(float fValue) {
|
||||
return (float) Math.atan(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* A direct call to Math.atan2.
|
||||
* @param fY
|
||||
* @param fX
|
||||
* @return Math.atan2(fY,fX)
|
||||
* @see java.lang.Math#atan2(double, double)
|
||||
*/
|
||||
public static float atan2(float fY, float fX) {
|
||||
return (float) Math.atan2(fY, fX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a fValue up. A call to Math.ceil
|
||||
* @param fValue The value.
|
||||
* @return The fValue rounded up
|
||||
* @see java.lang.Math#ceil(double)
|
||||
*/
|
||||
public static int ceil(float fValue) {
|
||||
return (int) Math.ceil(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast Trig functions for x86. This forces the trig functiosn to stay
|
||||
* within the safe area on the x86 processor (-45 degrees to +45 degrees)
|
||||
* The results may be very slightly off from what the Math and StrictMath
|
||||
* trig functions give due to rounding in the angle reduction but it will be
|
||||
* very very close.
|
||||
*
|
||||
* note: code from wiki posting on java.net by jeffpk
|
||||
*/
|
||||
public static float reduceSinAngle(float radians) {
|
||||
radians %= TWO_PI; // put us in -2PI to +2PI space
|
||||
if (Math.abs(radians) > PI) { // put us in -PI to +PI space
|
||||
radians = radians - (TWO_PI);
|
||||
}
|
||||
if (Math.abs(radians) > HALF_PI) {// put us in -PI/2 to +PI/2 space
|
||||
radians = PI - radians;
|
||||
}
|
||||
|
||||
return radians;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sine of a value.
|
||||
*
|
||||
* note: code from wiki posting on java.net by jeffpk
|
||||
*
|
||||
* @param fValue
|
||||
* The value to sine, in radians.
|
||||
* @return The sine of fValue.
|
||||
* @see java.lang.Math#sin(double)
|
||||
*/
|
||||
public static float sin2(float fValue) {
|
||||
fValue = reduceSinAngle(fValue); // limits angle to between -PI/2 and +PI/2
|
||||
if (Math.abs(fValue) <= Math.PI / 4) {
|
||||
return (float) Math.sin(fValue);
|
||||
}
|
||||
|
||||
return (float) Math.cos(Math.PI / 2 - fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cos of a value.
|
||||
*
|
||||
* @param fValue
|
||||
* The value to cosine, in radians.
|
||||
* @return The cosine of fValue.
|
||||
* @see java.lang.Math#cos(double)
|
||||
*/
|
||||
public static float cos2(float fValue) {
|
||||
return sin2(fValue + HALF_PI);
|
||||
}
|
||||
|
||||
public static float cos(float v) {
|
||||
return (float) Math.cos(v);
|
||||
}
|
||||
|
||||
public static float sin(float v) {
|
||||
return (float) Math.sin(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns E^fValue
|
||||
* @param fValue Value to raise to a power.
|
||||
* @return The value E^fValue
|
||||
* @see java.lang.Math#exp(double)
|
||||
*/
|
||||
public static float exp(float fValue) {
|
||||
return (float) Math.exp(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Absolute value of a float.
|
||||
* @param fValue The value to abs.
|
||||
* @return The abs of the value.
|
||||
* @see java.lang.Math#abs(float)
|
||||
*/
|
||||
public static float abs(float fValue) {
|
||||
if (fValue < 0) {
|
||||
return -fValue;
|
||||
}
|
||||
return fValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number rounded down.
|
||||
* @param fValue The value to round
|
||||
* @return The given number rounded down
|
||||
* @see java.lang.Math#floor(double)
|
||||
*/
|
||||
public static int floor(float fValue) {
|
||||
return (int) Math.floor(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1/sqrt(fValue)
|
||||
* @param fValue The value to process.
|
||||
* @return 1/sqrt(fValue)
|
||||
* @see java.lang.Math#sqrt(double)
|
||||
*/
|
||||
public static float invSqrt(float fValue) {
|
||||
return (float) (1.0f / Math.sqrt(fValue));
|
||||
}
|
||||
|
||||
public static float fastInvSqrt(float x) {
|
||||
float xhalf = 0.5f * x;
|
||||
int i = Float.floatToIntBits(x); // get bits for floating value
|
||||
i = 0x5f375a86 - (i >> 1); // gives initial guess y0
|
||||
x = Float.intBitsToFloat(i); // convert bits back to float
|
||||
x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the log base E of a value.
|
||||
* @param fValue The value to log.
|
||||
* @return The log of fValue base E
|
||||
* @see java.lang.Math#log(double)
|
||||
*/
|
||||
public static float log(float fValue) {
|
||||
return (float) Math.log(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the logarithm of value with given base, calculated as log(value)/log(base),
|
||||
* so that pow(base, return)==value (contributed by vear)
|
||||
* @param value The value to log.
|
||||
* @param base Base of logarithm.
|
||||
* @return The logarithm of value with given base
|
||||
*/
|
||||
public static float log(float value, float base) {
|
||||
return (float) (Math.log(value) / Math.log(base));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number raised to an exponent power. fBase^fExponent
|
||||
* @param fBase The base value (IE 2)
|
||||
* @param fExponent The exponent value (IE 3)
|
||||
* @return base raised to exponent (IE 8)
|
||||
* @see java.lang.Math#pow(double, double)
|
||||
*/
|
||||
public static float pow(float fBase, float fExponent) {
|
||||
return (float) Math.pow(fBase, fExponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value squared. fValue ^ 2
|
||||
* @param fValue The vaule to square.
|
||||
* @return The square of the given value.
|
||||
*/
|
||||
public static float sqr(float fValue) {
|
||||
return fValue * fValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the square root of a given value.
|
||||
* @param fValue The value to sqrt.
|
||||
* @return The square root of the given value.
|
||||
* @see java.lang.Math#sqrt(double)
|
||||
*/
|
||||
public static float sqrt(float fValue) {
|
||||
return (float) Math.sqrt(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tangent of a value. If USE_FAST_TRIG is enabled, an approximate value
|
||||
* is returned. Otherwise, a direct value is used.
|
||||
* @param fValue The value to tangent, in radians.
|
||||
* @return The tangent of fValue.
|
||||
* @see java.lang.Math#tan(double)
|
||||
*/
|
||||
public static float tan(float fValue) {
|
||||
return (float) Math.tan(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise
|
||||
* @param iValue The integer to examine.
|
||||
* @return The integer's sign.
|
||||
*/
|
||||
public static int sign(int iValue) {
|
||||
if (iValue > 0) {
|
||||
return 1;
|
||||
}
|
||||
if (iValue < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise
|
||||
* @param fValue The float to examine.
|
||||
* @return The float's sign.
|
||||
*/
|
||||
public static float sign(float fValue) {
|
||||
return Math.signum(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given 3 points in a 2d plane, this function computes if the points going from A-B-C
|
||||
* are moving counter clock wise.
|
||||
* @param p0 Point 0.
|
||||
* @param p1 Point 1.
|
||||
* @param p2 Point 2.
|
||||
* @return 1 If they are CCW, -1 if they are not CCW, 0 if p2 is between p0 and p1.
|
||||
*/
|
||||
public static int counterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) {
|
||||
float dx1, dx2, dy1, dy2;
|
||||
dx1 = p1.x - p0.x;
|
||||
dy1 = p1.y - p0.y;
|
||||
dx2 = p2.x - p0.x;
|
||||
dy2 = p2.y - p0.y;
|
||||
if (dx1 * dy2 > dy1 * dx2) {
|
||||
return 1;
|
||||
}
|
||||
if (dx1 * dy2 < dy1 * dx2) {
|
||||
return -1;
|
||||
}
|
||||
if ((dx1 * dx2 < 0) || (dy1 * dy2 < 0)) {
|
||||
return -1;
|
||||
}
|
||||
if ((dx1 * dx1 + dy1 * dy1) < (dx2 * dx2 + dy2 * dy2)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a point is inside a triangle. 1 if the point is on the ccw side,
|
||||
* -1 if the point is on the cw side, and 0 if it is on neither.
|
||||
* @param t0 First point of the triangle.
|
||||
* @param t1 Second point of the triangle.
|
||||
* @param t2 Third point of the triangle.
|
||||
* @param p The point to test.
|
||||
* @return Value 1 or -1 if inside triangle, 0 otherwise.
|
||||
*/
|
||||
public static int pointInsideTriangle(Vector2f t0, Vector2f t1, Vector2f t2, Vector2f p) {
|
||||
int val1 = counterClockwise(t0, t1, p);
|
||||
if (val1 == 0) {
|
||||
return 1;
|
||||
}
|
||||
int val2 = counterClockwise(t1, t2, p);
|
||||
if (val2 == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (val2 != val1) {
|
||||
return 0;
|
||||
}
|
||||
int val3 = counterClockwise(t2, t0, p);
|
||||
if (val3 == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (val3 != val1) {
|
||||
return 0;
|
||||
}
|
||||
return val3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the determinant of a 4x4 matrix.
|
||||
*/
|
||||
public static float determinant(double m00, double m01, double m02,
|
||||
double m03, double m10, double m11, double m12, double m13,
|
||||
double m20, double m21, double m22, double m23, double m30,
|
||||
double m31, double m32, double m33) {
|
||||
|
||||
double det01 = m20 * m31 - m21 * m30;
|
||||
double det02 = m20 * m32 - m22 * m30;
|
||||
double det03 = m20 * m33 - m23 * m30;
|
||||
double det12 = m21 * m32 - m22 * m31;
|
||||
double det13 = m21 * m33 - m23 * m31;
|
||||
double det23 = m22 * m33 - m23 * m32;
|
||||
return (float) (m00 * (m11 * det23 - m12 * det13 + m13 * det12) - m01
|
||||
* (m10 * det23 - m12 * det03 + m13 * det02) + m02
|
||||
* (m10 * det13 - m11 * det03 + m13 * det01) - m03
|
||||
* (m10 * det12 - m11 * det02 + m12 * det01));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random float between 0 and 1.
|
||||
*
|
||||
* @return A random float between <tt>0.0f</tt> (inclusive) to
|
||||
* <tt>1.0f</tt> (exclusive).
|
||||
*/
|
||||
public static float nextRandomFloat() {
|
||||
return rand.nextFloat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random float between min and max.
|
||||
*
|
||||
* @return A random int between <tt>min</tt> (inclusive) to
|
||||
* <tt>max</tt> (inclusive).
|
||||
*/
|
||||
public static int nextRandomInt(int min, int max) {
|
||||
return (int) (nextRandomFloat() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
public static int nextRandomInt() {
|
||||
return rand.nextInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a point from Spherical coordinates to Cartesian (using positive
|
||||
* Y as up) and stores the results in the store var.
|
||||
*/
|
||||
public static Vector3f sphericalToCartesian(Vector3f sphereCoords,
|
||||
Vector3f store) {
|
||||
store.y = sphereCoords.x * FastMath.sin(sphereCoords.z);
|
||||
float a = sphereCoords.x * FastMath.cos(sphereCoords.z);
|
||||
store.x = a * FastMath.cos(sphereCoords.y);
|
||||
store.z = a * FastMath.sin(sphereCoords.y);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a point from Cartesian coordinates (using positive Y as up) to
|
||||
* Spherical and stores the results in the store var. (Radius, Azimuth,
|
||||
* Polar)
|
||||
*/
|
||||
public static Vector3f cartesianToSpherical(Vector3f cartCoords,
|
||||
Vector3f store) {
|
||||
if (cartCoords.x == 0) {
|
||||
cartCoords.x = FastMath.FLT_EPSILON;
|
||||
}
|
||||
store.x = FastMath.sqrt((cartCoords.x * cartCoords.x)
|
||||
+ (cartCoords.y * cartCoords.y)
|
||||
+ (cartCoords.z * cartCoords.z));
|
||||
store.y = FastMath.atan(cartCoords.z / cartCoords.x);
|
||||
if (cartCoords.x < 0) {
|
||||
store.y += FastMath.PI;
|
||||
}
|
||||
store.z = FastMath.asin(cartCoords.y / store.x);
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a point from Spherical coordinates to Cartesian (using positive
|
||||
* Z as up) and stores the results in the store var.
|
||||
*/
|
||||
public static Vector3f sphericalToCartesianZ(Vector3f sphereCoords,
|
||||
Vector3f store) {
|
||||
store.z = sphereCoords.x * FastMath.sin(sphereCoords.z);
|
||||
float a = sphereCoords.x * FastMath.cos(sphereCoords.z);
|
||||
store.x = a * FastMath.cos(sphereCoords.y);
|
||||
store.y = a * FastMath.sin(sphereCoords.y);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a point from Cartesian coordinates (using positive Z as up) to
|
||||
* Spherical and stores the results in the store var. (Radius, Azimuth,
|
||||
* Polar)
|
||||
*/
|
||||
public static Vector3f cartesianZToSpherical(Vector3f cartCoords,
|
||||
Vector3f store) {
|
||||
if (cartCoords.x == 0) {
|
||||
cartCoords.x = FastMath.FLT_EPSILON;
|
||||
}
|
||||
store.x = FastMath.sqrt((cartCoords.x * cartCoords.x)
|
||||
+ (cartCoords.y * cartCoords.y)
|
||||
+ (cartCoords.z * cartCoords.z));
|
||||
store.z = FastMath.atan(cartCoords.z / cartCoords.x);
|
||||
if (cartCoords.x < 0) {
|
||||
store.z += FastMath.PI;
|
||||
}
|
||||
store.y = FastMath.asin(cartCoords.y / store.x);
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an value and expresses it in terms of min to max.
|
||||
*
|
||||
* @param val -
|
||||
* the angle to normalize (in radians)
|
||||
* @return the normalized angle (also in radians)
|
||||
*/
|
||||
public static float normalize(float val, float min, float max) {
|
||||
if (Float.isInfinite(val) || Float.isNaN(val)) {
|
||||
return 0f;
|
||||
}
|
||||
float range = max - min;
|
||||
while (val > max) {
|
||||
val -= range;
|
||||
}
|
||||
while (val < min) {
|
||||
val += range;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x
|
||||
* the value whose sign is to be adjusted.
|
||||
* @param y
|
||||
* the value whose sign is to be used.
|
||||
* @return x with its sign changed to match the sign of y.
|
||||
*/
|
||||
public static float copysign(float x, float y) {
|
||||
if (y >= 0 && x <= -0) {
|
||||
return -x;
|
||||
} else if (y < 0 && x >= 0) {
|
||||
return -x;
|
||||
} else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a float input and clamp it between min and max.
|
||||
*
|
||||
* @param input
|
||||
* @param min
|
||||
* @param max
|
||||
* @return clamped input
|
||||
*/
|
||||
public static float clamp(float input, float min, float max) {
|
||||
return (input < min) ? min : (input > max) ? max : input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamps the given float to be between 0 and 1.
|
||||
*
|
||||
* @param input
|
||||
* @return input clamped between 0 and 1.
|
||||
*/
|
||||
public static float saturate(float input) {
|
||||
return clamp(input, 0f, 1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a single precision (32 bit) floating point value
|
||||
* into half precision (16 bit).
|
||||
*
|
||||
* Source: http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
|
||||
*
|
||||
* @param half The half floating point value as a short.
|
||||
* @return floating point value of the half.
|
||||
*/
|
||||
public static float convertHalfToFloat(short half) {
|
||||
switch ((int) half) {
|
||||
case 0x0000:
|
||||
return 0f;
|
||||
case 0x8000:
|
||||
return -0f;
|
||||
case 0x7c00:
|
||||
return Float.POSITIVE_INFINITY;
|
||||
case 0xfc00:
|
||||
return Float.NEGATIVE_INFINITY;
|
||||
// TODO: Support for NaN?
|
||||
default:
|
||||
return Float.intBitsToFloat(((half & 0x8000) << 16)
|
||||
| (((half & 0x7c00) + 0x1C000) << 13)
|
||||
| ((half & 0x03FF) << 13));
|
||||
}
|
||||
}
|
||||
|
||||
public static short convertFloatToHalf(float flt) {
|
||||
if (Float.isNaN(flt)) {
|
||||
throw new UnsupportedOperationException("NaN to half conversion not supported!");
|
||||
} else if (flt == Float.POSITIVE_INFINITY) {
|
||||
return (short) 0x7c00;
|
||||
} else if (flt == Float.NEGATIVE_INFINITY) {
|
||||
return (short) 0xfc00;
|
||||
} else if (flt == 0f) {
|
||||
return (short) 0x0000;
|
||||
} else if (flt == -0f) {
|
||||
return (short) 0x8000;
|
||||
} else if (flt > 65504f) {
|
||||
// max value supported by half float
|
||||
return 0x7bff;
|
||||
} else if (flt < -65504f) {
|
||||
return (short) (0x7bff | 0x8000);
|
||||
} else if (flt > 0f && flt < 5.96046E-8f) {
|
||||
return 0x0001;
|
||||
} else if (flt < 0f && flt > -5.96046E-8f) {
|
||||
return (short) 0x8001;
|
||||
}
|
||||
|
||||
int f = Float.floatToIntBits(flt);
|
||||
return (short) (((f >> 16) & 0x8000)
|
||||
| ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
|
||||
| ((f >> 13) & 0x03ff));
|
||||
}
|
||||
}
|
||||
743
src/com/jme3/math/Vector2f.java
Normal file
743
src/com/jme3/math/Vector2f.java
Normal file
@@ -0,0 +1,743 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jme3.math;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* <code>Vector2f</code> defines a Vector for a two float value vector.
|
||||
*
|
||||
* @author Mark Powell
|
||||
* @author Joshua Slack
|
||||
*/
|
||||
public final class Vector2f implements Cloneable {
|
||||
private static final Logger logger = Logger.getLogger(Vector2f.class.getName());
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final Vector2f ZERO = new Vector2f(0f, 0f);
|
||||
public static final Vector2f UNIT_XY = new Vector2f(1f, 1f);
|
||||
|
||||
/**
|
||||
* the x value of the vector.
|
||||
*/
|
||||
public float x;
|
||||
/**
|
||||
* the y value of the vector.
|
||||
*/
|
||||
public float y;
|
||||
|
||||
/**
|
||||
* Creates a Vector2f with the given initial x and y values.
|
||||
*
|
||||
* @param x
|
||||
* The x value of this Vector2f.
|
||||
* @param y
|
||||
* The y value of this Vector2f.
|
||||
*/
|
||||
public Vector2f(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Vector2f with x and y set to 0. Equivalent to Vector2f(0,0).
|
||||
*/
|
||||
public Vector2f() {
|
||||
x = y = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Vector2f that contains the passed vector's information
|
||||
*
|
||||
* @param vector2f
|
||||
* The vector to copy
|
||||
*/
|
||||
public Vector2f(Vector2f vector2f) {
|
||||
this.x = vector2f.x;
|
||||
this.y = vector2f.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the x and y values of the vector
|
||||
*
|
||||
* @param x
|
||||
* the x value of the vector.
|
||||
* @param y
|
||||
* the y value of the vector.
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector2f set(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the x and y values of the vector from another vector
|
||||
*
|
||||
* @param vec
|
||||
* the vector to copy from
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector2f set(Vector2f vec) {
|
||||
this.x = vec.x;
|
||||
this.y = vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>add</code> adds a provided vector to this vector creating a
|
||||
* resultant vector which is returned. If the provided vector is null, null
|
||||
* is returned.
|
||||
*
|
||||
* @param vec
|
||||
* the vector to add to this.
|
||||
* @return the resultant vector.
|
||||
*/
|
||||
public Vector2f add(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
return new Vector2f(x + vec.x, y + vec.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds a provided vector to this vector internally,
|
||||
* and returns a handle to this vector for easy chaining of calls. If the
|
||||
* provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec
|
||||
* the vector to add to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f addLocal(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x += vec.x;
|
||||
y += vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds the provided values to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls.
|
||||
*
|
||||
* @param addX
|
||||
* value to add to x
|
||||
* @param addY
|
||||
* value to add to y
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f addLocal(float addX, float addY) {
|
||||
x += addX;
|
||||
y += addY;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>add</code> adds this vector by <code>vec</code> and stores the
|
||||
* result in <code>result</code>.
|
||||
*
|
||||
* @param vec
|
||||
* The vector to add.
|
||||
* @param result
|
||||
* The vector to store the result in.
|
||||
* @return The result vector, after adding.
|
||||
*/
|
||||
public Vector2f add(Vector2f vec, Vector2f result) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
if (result == null)
|
||||
result = new Vector2f();
|
||||
result.x = x + vec.x;
|
||||
result.y = y + vec.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>dot</code> calculates the dot product of this vector with a
|
||||
* provided vector. If the provided vector is null, 0 is returned.
|
||||
*
|
||||
* @param vec
|
||||
* the vector to dot with this vector.
|
||||
* @return the resultant dot product of this vector and a given vector.
|
||||
*/
|
||||
public float dot(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, 0 returned.");
|
||||
return 0;
|
||||
}
|
||||
return x * vec.x + y * vec.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>cross</code> calculates the cross product of this vector with a
|
||||
* parameter vector v.
|
||||
*
|
||||
* @param v
|
||||
* the vector to take the cross product of with this.
|
||||
* @return the cross product vector.
|
||||
*/
|
||||
public Vector3f cross(Vector2f v) {
|
||||
return new Vector3f(0, 0, determinant(v));
|
||||
}
|
||||
|
||||
public float determinant(Vector2f v) {
|
||||
return (x * v.y) - (y * v.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from this to the
|
||||
* finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
|
||||
*
|
||||
* @param finalVec
|
||||
* The final vector to interpolate towards
|
||||
* @param changeAmnt
|
||||
* An amount between 0.0 - 1.0 representing a percentage change
|
||||
* from this towards finalVec
|
||||
*/
|
||||
public Vector2f interpolate(Vector2f finalVec, float changeAmnt) {
|
||||
this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from beginVec to
|
||||
* finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
|
||||
*
|
||||
* @param beginVec
|
||||
* The begining vector (delta=0)
|
||||
* @param finalVec
|
||||
* The final vector to interpolate towards (delta=1)
|
||||
* @param changeAmnt
|
||||
* An amount between 0.0 - 1.0 representing a precentage change
|
||||
* from beginVec towards finalVec
|
||||
*/
|
||||
public Vector2f interpolate(Vector2f beginVec, Vector2f finalVec,
|
||||
float changeAmnt) {
|
||||
this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a vector... if it is null or its floats are NaN or infinite, return
|
||||
* false. Else return true.
|
||||
*
|
||||
* @param vector
|
||||
* the vector to check
|
||||
* @return true or false as stated above.
|
||||
*/
|
||||
public static boolean isValidVector(Vector2f vector) {
|
||||
if (vector == null) return false;
|
||||
if (Float.isNaN(vector.x) ||
|
||||
Float.isNaN(vector.y)) return false;
|
||||
if (Float.isInfinite(vector.x) ||
|
||||
Float.isInfinite(vector.y)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>length</code> calculates the magnitude of this vector.
|
||||
*
|
||||
* @return the length or magnitude of the vector.
|
||||
*/
|
||||
public float length() {
|
||||
return FastMath.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>lengthSquared</code> calculates the squared value of the
|
||||
* magnitude of the vector.
|
||||
*
|
||||
* @return the magnitude squared of the vector.
|
||||
*/
|
||||
public float lengthSquared() {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distanceSquared</code> calculates the distance squared between
|
||||
* this vector and vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance squared.
|
||||
* @return the distance squared between the two vectors.
|
||||
*/
|
||||
public float distanceSquared(Vector2f v) {
|
||||
double dx = x - v.x;
|
||||
double dy = y - v.y;
|
||||
return (float) (dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distanceSquared</code> calculates the distance squared between
|
||||
* this vector and vector v.
|
||||
*
|
||||
* @param otherX x-coord
|
||||
* @param otherY y-coord
|
||||
* @return the distance squared between the two vectors.
|
||||
*/
|
||||
public float distanceSquared(float otherX, float otherY) {
|
||||
double dx = x - otherX;
|
||||
double dy = y - otherY;
|
||||
return (float) (dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distance</code> calculates the distance between this vector and
|
||||
* vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance.
|
||||
* @return the distance between the two vectors.
|
||||
*/
|
||||
public float distance(Vector2f v) {
|
||||
return FastMath.sqrt(distanceSquared(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>mult</code> multiplies this vector by a scalar. The resultant
|
||||
* vector is returned.
|
||||
*
|
||||
* @param scalar
|
||||
* the value to multiply this vector by.
|
||||
* @return the new vector.
|
||||
*/
|
||||
public Vector2f mult(float scalar) {
|
||||
return new Vector2f(x * scalar, y * scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies this vector by a scalar internally,
|
||||
* and returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param scalar
|
||||
* the value to multiply this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f multLocal(float scalar) {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec
|
||||
* the vector to mult to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f multLocal(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x *= vec.x;
|
||||
y *= vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies this Vector2f's x and y by the scalar and stores the result in
|
||||
* product. The result is returned for chaining. Similar to
|
||||
* product=this*scalar;
|
||||
*
|
||||
* @param scalar
|
||||
* The scalar to multiply by.
|
||||
* @param product
|
||||
* The vector2f to store the result in.
|
||||
* @return product, after multiplication.
|
||||
*/
|
||||
public Vector2f mult(float scalar, Vector2f product) {
|
||||
if (null == product) {
|
||||
product = new Vector2f();
|
||||
}
|
||||
|
||||
product.x = x * scalar;
|
||||
product.y = y * scalar;
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divide</code> divides the values of this vector by a scalar and
|
||||
* returns the result. The values of this vector remain untouched.
|
||||
*
|
||||
* @param scalar
|
||||
* the value to divide this vectors attributes by.
|
||||
* @return the result <code>Vector</code>.
|
||||
*/
|
||||
public Vector2f divide(float scalar) {
|
||||
return new Vector2f(x / scalar, y / scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divideLocal</code> divides this vector by a scalar internally,
|
||||
* and returns a handle to this vector for easy chaining of calls. Dividing
|
||||
* by zero will result in an exception.
|
||||
*
|
||||
* @param scalar
|
||||
* the value to divides this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f divideLocal(float scalar) {
|
||||
x /= scalar;
|
||||
y /= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>negate</code> returns the negative of this vector. All values are
|
||||
* negated and set to a new vector.
|
||||
*
|
||||
* @return the negated vector.
|
||||
*/
|
||||
public Vector2f negate() {
|
||||
return new Vector2f(-x, -y);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>negateLocal</code> negates the internal values of this vector.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector2f negateLocal() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtract</code> subtracts the values of a given vector from those
|
||||
* of this vector creating a new vector object. If the provided vector is
|
||||
* null, an exception is thrown.
|
||||
*
|
||||
* @param vec
|
||||
* the vector to subtract from this vector.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector2f subtract(Vector2f vec) {
|
||||
return subtract(vec, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtract</code> subtracts the values of a given vector from those
|
||||
* of this vector storing the result in the given vector object. If the
|
||||
* provided vector is null, an exception is thrown.
|
||||
*
|
||||
* @param vec
|
||||
* the vector to subtract from this vector.
|
||||
* @param store
|
||||
* the vector to store the result in. It is safe for this to be
|
||||
* the same as vec. If null, a new vector is created.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector2f subtract(Vector2f vec, Vector2f store) {
|
||||
if (store == null)
|
||||
store = new Vector2f();
|
||||
store.x = x - vec.x;
|
||||
store.y = y - vec.y;
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtract</code> subtracts the given x,y values from those of this
|
||||
* vector creating a new vector object.
|
||||
*
|
||||
* @param valX
|
||||
* value to subtract from x
|
||||
* @param valY
|
||||
* value to subtract from y
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f subtract(float valX, float valY) {
|
||||
return new Vector2f(x - valX, y - valY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec
|
||||
* the vector to subtract
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f subtractLocal(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts the provided values from this
|
||||
* vector internally, and returns a handle to this vector for easy chaining
|
||||
* of calls.
|
||||
*
|
||||
* @param valX
|
||||
* value to subtract from x
|
||||
* @param valY
|
||||
* value to subtract from y
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f subtractLocal(float valX, float valY) {
|
||||
x -= valX;
|
||||
y -= valY;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalize</code> returns the unit vector of this vector.
|
||||
*
|
||||
* @return unit vector of this vector.
|
||||
*/
|
||||
public Vector2f normalize() {
|
||||
float length = length();
|
||||
if (length != 0) {
|
||||
return divide(length);
|
||||
}
|
||||
|
||||
return divide(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalizeLocal</code> makes this vector into a unit vector of
|
||||
* itself.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector2f normalizeLocal() {
|
||||
float length = length();
|
||||
if (length != 0) {
|
||||
return divideLocal(length);
|
||||
}
|
||||
|
||||
return divideLocal(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>smallestAngleBetween</code> returns (in radians) the minimum
|
||||
* angle between two vectors. It is assumed that both this vector and the
|
||||
* given vector are unit vectors (iow, normalized).
|
||||
*
|
||||
* @param otherVector
|
||||
* a unit vector to find the angle against
|
||||
* @return the angle in radians.
|
||||
*/
|
||||
public float smallestAngleBetween(Vector2f otherVector) {
|
||||
float dotProduct = dot(otherVector);
|
||||
float angle = FastMath.acos(dotProduct);
|
||||
return angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>angleBetween</code> returns (in radians) the angle required to
|
||||
* rotate a ray represented by this vector to lie colinear to a ray
|
||||
* described by the given vector. It is assumed that both this vector and
|
||||
* the given vector are unit vectors (iow, normalized).
|
||||
*
|
||||
* @param otherVector
|
||||
* the "destination" unit vector
|
||||
* @return the angle in radians.
|
||||
*/
|
||||
public float angleBetween(Vector2f otherVector) {
|
||||
float angle = FastMath.atan2(otherVector.y, otherVector.x)
|
||||
- FastMath.atan2(y, x);
|
||||
return angle;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public Vector2f setX(float x) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public Vector2f setY(float y) {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>getAngle</code> returns (in radians) the angle represented by
|
||||
* this Vector2f as expressed by a conversion from rectangular coordinates (<code>x</code>, <code>y</code>)
|
||||
* to polar coordinates (r, <i>theta</i>).
|
||||
*
|
||||
* @return the angle in radians. [-pi, pi)
|
||||
*/
|
||||
public float getAngle() {
|
||||
return -FastMath.atan2(y, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>zero</code> resets this vector's data to zero internally.
|
||||
*/
|
||||
public Vector2f zero() {
|
||||
x = y = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>hashCode</code> returns a unique code for this vector object
|
||||
* based on it's values. If two vectors are logically equivalent, they will
|
||||
* return the same hash code value.
|
||||
*
|
||||
* @return the hash code value of this vector.
|
||||
*/
|
||||
public int hashCode() {
|
||||
int hash = 37;
|
||||
hash += 37 * hash + Float.floatToIntBits(x);
|
||||
hash += 37 * hash + Float.floatToIntBits(y);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2f clone() {
|
||||
try {
|
||||
return (Vector2f) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError(); // can not happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this Vector2f into the given float[] object.
|
||||
*
|
||||
* @param floats
|
||||
* The float[] to take this Vector2f. If null, a new float[2] is
|
||||
* created.
|
||||
* @return The array, with X, Y float values in that order
|
||||
*/
|
||||
public float[] toArray(float[] floats) {
|
||||
if (floats == null) {
|
||||
floats = new float[2];
|
||||
}
|
||||
floats[0] = x;
|
||||
floats[1] = y;
|
||||
return floats;
|
||||
}
|
||||
|
||||
/**
|
||||
* are these two vectors the same? they are is they both have the same x and
|
||||
* y values.
|
||||
*
|
||||
* @param o
|
||||
* the object to compare for equality
|
||||
* @return true if they are equal
|
||||
*/
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Vector2f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector2f comp = (Vector2f) o;
|
||||
if (Float.compare(x, comp.x) != 0)
|
||||
return false;
|
||||
if (Float.compare(y, comp.y) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>toString</code> returns the string representation of this vector
|
||||
* object. The format of the string is such: com.jme.math.Vector2f
|
||||
* [X=XX.XXXX, Y=YY.YYYY]
|
||||
*
|
||||
* @return the string representation of this vector.
|
||||
*/
|
||||
public String toString() {
|
||||
return "(" + x + ", " + y + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Used with serialization. Not to be called manually.
|
||||
*
|
||||
* @param in
|
||||
* ObjectInput
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
* @see java.io.Externalizable
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
x = in.readFloat();
|
||||
y = in.readFloat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used with serialization. Not to be called manually.
|
||||
*
|
||||
* @param out
|
||||
* ObjectOutput
|
||||
* @throws IOException
|
||||
* @see java.io.Externalizable
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeFloat(x);
|
||||
out.writeFloat(y);
|
||||
}
|
||||
|
||||
public void rotateAroundOrigin(float angle, boolean cw) {
|
||||
if (cw)
|
||||
angle = -angle;
|
||||
float newX = FastMath.cos(angle) * x - FastMath.sin(angle) * y;
|
||||
float newY = FastMath.sin(angle) * x + FastMath.cos(angle) * y;
|
||||
x = newX;
|
||||
y = newY;
|
||||
}
|
||||
}
|
||||
1031
src/com/jme3/math/Vector3f.java
Normal file
1031
src/com/jme3/math/Vector3f.java
Normal file
File diff suppressed because it is too large
Load Diff
284
src/shader/MultiTex.java
Executable file
284
src/shader/MultiTex.java
Executable file
@@ -0,0 +1,284 @@
|
||||
package shader;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Renderable;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.opengl.InternalTextureLoader;
|
||||
import org.newdawn.slick.opengl.Texture;
|
||||
|
||||
/**
|
||||
* Class to support the concept of a single artifact being
|
||||
* comprised of multiple image resources.</br>
|
||||
* For example a colourmap, normalmap, diffusemap, and specularmap.
|
||||
* This is currently extremely buggy, and I don't know why some
|
||||
* things have to be the way the are.
|
||||
* @author Chronocide (Jeremy Klix)
|
||||
*
|
||||
*
|
||||
* All drawing is done starting from the top left vertex and
|
||||
* moving counter clockwise.</br>
|
||||
*/
|
||||
//TODO Make interface feel a little more like the familiar Image class
|
||||
//TODO Determine a method of dealing with the case were textures
|
||||
//are not all the same size. For instance should textures be
|
||||
//stretched, tiled, clamped?
|
||||
//TODO Way of handling images larger then the supporting cards
|
||||
//max texture size ala Slicks BigImage class.
|
||||
//TODO Needs way more attention to documenting inheritance.
|
||||
//TODO Test using different numbers of textures.
|
||||
public class MultiTex implements Renderable{
|
||||
private static int units = -1;
|
||||
|
||||
/** The top left corner identifier */
|
||||
public static final int TOP_LEFT = 0;
|
||||
/** The top right corner identifier */
|
||||
public static final int BOTTOM_LEFT = 1;
|
||||
/** The bottom left corner identifier */
|
||||
public static final int BOTTOM_RIGHT = 3;
|
||||
/** The bottom right corner identifier */
|
||||
public static final int TOP_RIGHT = 2;
|
||||
|
||||
|
||||
private List<Texture> textures;
|
||||
|
||||
private int primaryTextureIndex = 0;
|
||||
//Width and height based off primary texture loaded
|
||||
private float imgWidth, imgHeight;
|
||||
//Primary texture width and height clamped between 0 and 1
|
||||
private float texWidth, texHeight;
|
||||
|
||||
|
||||
private float[] normals = new float[]{0,0,1,
|
||||
0,0,1,
|
||||
0,0,1,
|
||||
0,0,1};
|
||||
private float[] colours = new float[]{1,1,1,1,
|
||||
1,1,1,1,
|
||||
1,1,1,1,
|
||||
1,1,1,1};
|
||||
|
||||
/**
|
||||
* Constructs a new <tt>MultiTex</tt> object using the textures
|
||||
* identified in <tt>textures</tt>.</br>
|
||||
* The index of the textures in the list will be the texture unit
|
||||
* that the texture is bound to.</br>
|
||||
* @param textures a list of paths to the textures to use.
|
||||
* @throws SlickException If <tt>textures.size()</tt> is greater
|
||||
* than the maximum number of texture units.
|
||||
*/
|
||||
public MultiTex(List<String> textures)throws SlickException{
|
||||
//Check how many texture units are supported
|
||||
if(units==-1){
|
||||
units = GL11.glGetInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS);
|
||||
System.out.println(units);
|
||||
}
|
||||
if(units < textures.size()){
|
||||
throw new UnsupportedOperationException("You attempted to " +
|
||||
"create an artifact with " + textures.size() +
|
||||
" textures, but your environment only supports " +
|
||||
units + " texure image units.");
|
||||
}
|
||||
|
||||
//Create texture list
|
||||
this.textures = new ArrayList<Texture>(textures.size());
|
||||
|
||||
|
||||
|
||||
//Load textures into texture list.
|
||||
InternalTextureLoader itl = InternalTextureLoader.get();
|
||||
for(int i = 0; i<textures.size(); i++){
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0 + i);
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
try{
|
||||
this.textures.add(itl.getTexture(textures.get(i),
|
||||
false,
|
||||
GL11.GL_LINEAR,
|
||||
null));
|
||||
}catch(IOException e){
|
||||
throw new SlickException(e.getMessage());
|
||||
}
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
//FIXME pretty sure there is a rather serious problem here.
|
||||
//Since the TextureLoader used keeps track of previously loaded
|
||||
//textures, and binds them to a unit at creation. If a single
|
||||
//image is loaded twice to two different Texture Units, it may
|
||||
//not actually be associated with the correct unit on the
|
||||
//second load. This is because the TextureLoader will simply
|
||||
//return the earlier loaded texture.
|
||||
|
||||
//Reset current texture unit to 0
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
imgWidth = this.textures.get(primaryTextureIndex).getImageWidth();
|
||||
imgHeight = this.textures.get(primaryTextureIndex).getImageHeight();
|
||||
texWidth = this.textures.get(primaryTextureIndex).getWidth();
|
||||
texHeight = this.textures.get(primaryTextureIndex).getHeight();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public MultiTex(String[] textures) throws SlickException{
|
||||
this(Arrays.asList(textures));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public MultiTex(String t1, String t2)throws SlickException{
|
||||
this(new String[]{t1,t2});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* When extending please note that this method relies on the
|
||||
* private method drawEmbedded.</br>
|
||||
*/
|
||||
public void draw(float x, float y){
|
||||
draw(x, y, x + imgWidth, y+imgHeight,
|
||||
0, 0, imgWidth, imgHeight);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Draw a section of this MultTex at a particular location and
|
||||
* scale on the screen.</br>
|
||||
*
|
||||
* This is the draw method that all other overloaded draw
|
||||
* methods eventually evoke.</br>
|
||||
*
|
||||
* @param x1
|
||||
* @param y1
|
||||
* @param x2
|
||||
* @param y2
|
||||
* @param sx1
|
||||
* @param sy1
|
||||
* @param sx2
|
||||
* @param sy2
|
||||
*/
|
||||
public void draw(float x1, float y1, float x2, float y2,
|
||||
float sx1, float sy1, float sx2, float sy2){
|
||||
|
||||
//Bind textures to their correct locations
|
||||
for(int i = 0; i < textures.size(); i++){
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0 + i);
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textures.get(i).getTextureID());
|
||||
}
|
||||
|
||||
GL11.glBegin(GL11.GL_QUADS);
|
||||
drawEmbedded(x1, y1, x2, y2,
|
||||
sx1, sy1, sx2, sy2);
|
||||
GL11.glEnd();
|
||||
|
||||
//Clean up texture setting to allow basic slick to operate correctly.
|
||||
for(int i = textures.size()-1; i>=0; i--){
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0+i);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void draw(float x1, float y1, float x2, float y2,
|
||||
float sx1, float sy1, float sx2, float sy2,
|
||||
Color c){
|
||||
float[] bu = colours;//Save the colour state
|
||||
|
||||
setColour(c);
|
||||
draw(x1, y1, x2, y2, sx1, sy1, sx2, sy2);
|
||||
|
||||
colours = bu;//Restore the colour state
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the colour of a given corner.</br>
|
||||
* Note that this will have an effect only if: the
|
||||
* fixed pixel pipeline is being used; or the applied shader
|
||||
* takes the vertex colour into account.</br>
|
||||
* @param corner
|
||||
* @param c
|
||||
*/
|
||||
public void setColour(int corner, Color c){
|
||||
colours[corner*4 + 0] = c.r;
|
||||
colours[corner*4 + 1] = c.g;
|
||||
colours[corner*4 + 2] = c.b;
|
||||
colours[corner*4 + 3] = c.a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the colour of all four corners.</br>
|
||||
* @param c
|
||||
*/
|
||||
public void setColour(Color c){
|
||||
for(int i=0; i<4; i++){
|
||||
setColour(i, c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void drawEmbedded(float x1, float y1, float x2, float y2,
|
||||
float sx1, float sy1, float sx2, float sy2){
|
||||
//TODO reduce code duplication need to produce sequence 0,1,3,2
|
||||
|
||||
//TOP LEFT
|
||||
for(int i=0; i<textures.size(); i++){
|
||||
GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0 + i,
|
||||
(sx1/imgWidth) * texWidth,
|
||||
(sy1/imgHeight)* texHeight);
|
||||
}
|
||||
GL11.glColor4f(colours[0], colours[1], colours[2], colours[3]);
|
||||
GL11.glNormal3f(normals[0], normals[1], normals[2]);
|
||||
GL11.glVertex3f(x1, y1, 0);
|
||||
|
||||
//BOTTOM LEFT
|
||||
for(int i=0; i<textures.size(); i++){
|
||||
GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0 + i,
|
||||
(sx1/imgWidth) * texWidth,
|
||||
(sy2/imgHeight)* texHeight);
|
||||
}
|
||||
GL11.glColor4f(colours[3], colours[5], colours[6], colours[7]);
|
||||
GL11.glNormal3f(normals[3], normals[4], normals[5]);
|
||||
GL11.glVertex3f(x1, y2, 0);
|
||||
|
||||
//BOTTOM RIGHT
|
||||
for(int i=0; i<textures.size(); i++){
|
||||
GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0 + i,
|
||||
(sx2/imgWidth) * texWidth,
|
||||
(sy2/imgHeight)* texHeight);
|
||||
}
|
||||
GL11.glColor4f(colours[8], colours[9], colours[10], colours[11]);
|
||||
GL11.glNormal3f(normals[6], normals[7], normals[8]);
|
||||
GL11.glVertex3f(x2, y2, 0);
|
||||
|
||||
//TOP RIGHT
|
||||
for(int i=0; i<textures.size(); i++){
|
||||
GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0 + i,
|
||||
(sx2/imgWidth) * texWidth,
|
||||
(sy1/imgHeight)* texHeight);
|
||||
}
|
||||
GL11.glColor4f(colours[12], colours[13], colours[14], colours[15]);
|
||||
GL11.glNormal3f(normals[9], normals[10], normals[11]);
|
||||
GL11.glVertex3f(x2, y1, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
453
src/shader/Shader.java
Executable file
453
src/shader/Shader.java
Executable file
@@ -0,0 +1,453 @@
|
||||
package shader;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Class used to use and access shaders without having to deal
|
||||
* with all of the fidly openGL bits.
|
||||
* @author Chronocide (Jeremy Klix)
|
||||
*
|
||||
*/
|
||||
public class Shader {
|
||||
public static final int BRIEF = 128;
|
||||
public static final int MODERATE = 512;
|
||||
public static final int VERBOSE = 1024;
|
||||
private static final int NOT_LOADED = -1;
|
||||
private static final String ERR_LOCATION =
|
||||
"Warning: variable %s could not be found. " +
|
||||
"Ensure the name is spelled correctly\n";
|
||||
private static int logging = MODERATE;
|
||||
|
||||
private ShaderResourceManager srm;
|
||||
/**
|
||||
* ID of the <tt>Shader</tt>. A Shader may have programID of
|
||||
* -1 only before construction is completed, or
|
||||
* after the <tt>Shader</tt> is deleted
|
||||
*/
|
||||
private int programID = NOT_LOADED;
|
||||
private Map<String, ShaderVariable> vars = new HashMap<String, ShaderVariable>();
|
||||
|
||||
|
||||
private Shader(ShaderResourceManager srm,
|
||||
Collection<String> vertex,
|
||||
Collection<String> fragment)throws SlickException{
|
||||
this.srm = srm;
|
||||
StringBuilder errorMessage = new StringBuilder();
|
||||
|
||||
programID = GL20.glCreateProgram();
|
||||
|
||||
int[] shaderIds = new int[vertex.size() + fragment.size()];
|
||||
int index = 0;
|
||||
|
||||
//Load Vertex Shaders
|
||||
for(String vertShader: vertex){
|
||||
int vsid = srm.getVertexShaderID(vertShader);
|
||||
srm.createProgramShaderDependancy(programID, vsid);
|
||||
|
||||
//Add to shader ids array
|
||||
shaderIds[index] = vsid;
|
||||
index++;
|
||||
|
||||
//Check for errors with shader
|
||||
if(!compiledSuccessfully(vsid)){
|
||||
errorMessage.append("Vertex Shader ");
|
||||
errorMessage.append(vertShader);
|
||||
errorMessage.append(" failed to compile.\n");
|
||||
errorMessage.append(getShaderInfoLog(vsid));
|
||||
errorMessage.append("\n\n");
|
||||
}
|
||||
|
||||
scanSource(vertShader);
|
||||
}
|
||||
|
||||
|
||||
//Load Fragment Shaders
|
||||
for(String fragShader: fragment){
|
||||
int fsid = srm.getFragementShaderID(fragShader);
|
||||
srm.createProgramShaderDependancy(programID, fsid);
|
||||
|
||||
//Add to shader ids array
|
||||
shaderIds[index] = fsid;
|
||||
index++;
|
||||
|
||||
//Check for errors with shader
|
||||
if(!compiledSuccessfully(fsid)){
|
||||
errorMessage.append("Fragment Shader ");
|
||||
errorMessage.append(fragShader);
|
||||
errorMessage.append(" failed to compile.\n");
|
||||
errorMessage.append(getShaderInfoLog(fsid));
|
||||
errorMessage.append("\n\n");
|
||||
}
|
||||
|
||||
scanSource(fragShader);
|
||||
}
|
||||
|
||||
//Attach shaders to program
|
||||
for(int i=0; i<index; i++){
|
||||
GL20.glAttachShader(programID, shaderIds[i]);
|
||||
}
|
||||
//Link program
|
||||
GL20.glLinkProgram(programID);
|
||||
if(!linkedSuccessfully()){
|
||||
errorMessage.append("Linking Error\n");
|
||||
errorMessage.append(getProgramInfoLog());
|
||||
errorMessage.append("\n\n");
|
||||
}
|
||||
|
||||
if(errorMessage.length()!=0){
|
||||
errorMessage.insert(0, "Could not compile shader.\n");
|
||||
srm.removeProgram(programID);
|
||||
programID = -1;
|
||||
errorMessage.append("Stack Trace:");
|
||||
throw new SlickException(errorMessage.toString());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Factory method to create a new Shader.
|
||||
* @param vertexFileName
|
||||
* @param fragmentFileName
|
||||
* @return
|
||||
* @throws SlickException
|
||||
*/
|
||||
public static Shader makeShader(String vertexFileName,
|
||||
String fragmentFileName)throws SlickException{
|
||||
ArrayList<String> l1 = new ArrayList<String>();
|
||||
l1.add(vertexFileName);
|
||||
ArrayList<String> l2 = new ArrayList<String>();
|
||||
l2.add(fragmentFileName);
|
||||
|
||||
return new Shader(ShaderResourceManagerImpl.getSRM(),
|
||||
l1,
|
||||
l2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reverts GL context back to the fixed pixel pipeline.<br>
|
||||
*/
|
||||
public static void forceFixedShader(){
|
||||
GL20.glUseProgram(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of characters to be returned when printing
|
||||
* errors.</br> Suggested values are the constants
|
||||
* <tt>BRIEF</tt>, <tt>MODERATE</tt>, and <tt>VERBOSE</tt>.</br>
|
||||
* @param detailLevel number of characters to display for error
|
||||
* messages.
|
||||
*/
|
||||
public static void setLoggingDetail(int detailLevel){
|
||||
logging = detailLevel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deletes this shader and unloads all free resources.</br>
|
||||
* TODO should this be called from <tt>finalise()</tt>, or is
|
||||
* that just asking for trouble?
|
||||
*/
|
||||
public void deleteShader(){
|
||||
srm.removeProgram(programID);
|
||||
programID = NOT_LOADED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this <tt>Shader</tt> has been deleted.</br>
|
||||
* @return true if this <tt>Shader</tt> has been deleted.</br>
|
||||
*/
|
||||
public boolean isDeleted(){
|
||||
return programID == NOT_LOADED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Activates the shader.</br>
|
||||
*/
|
||||
public void startShader(){
|
||||
if(programID == NOT_LOADED){
|
||||
throw new IllegalStateException("Cannot start shader; this" +
|
||||
" Shader has been deleted");
|
||||
}
|
||||
forceFixedShader(); //Not sure why this is necessary but it is.
|
||||
GL20.glUseProgram(programID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//UNIFORM SETTERS
|
||||
/**
|
||||
* Sets the value of the uniform integer Variable <tt>name</tt>.</br>
|
||||
* @param name the variable to set.
|
||||
* @param value the value to be set.
|
||||
*/
|
||||
public Shader setUniformIntVariable(String name, int value){
|
||||
return setUniformIntVariable(name, new int[]{value});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Shader setUniformIntVariable(String name, int v0, int v1){
|
||||
return setUniformIntVariable(name, new int[]{v0, v1});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Shader setUniformIntVariable(String name,
|
||||
int v0, int v1, int v2){
|
||||
return setUniformIntVariable(name, new int[]{v0, v1, v2});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Shader setUniformIntVariable(String name,
|
||||
int v0, int v1, int v2, int v3){
|
||||
return setUniformIntVariable(name, new int[]{v0, v1, v2, v3});
|
||||
}
|
||||
|
||||
|
||||
public Shader setUniformIntVariable(String name, int[] values){
|
||||
ShaderVariable var = vars.get(name);
|
||||
if(var==null){
|
||||
printError(name);
|
||||
}else{
|
||||
var.setUniformValue(values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the value of the uniform integer Variable
|
||||
* <tt>name</tt>.</br>
|
||||
* @param name the variable to set.
|
||||
* @param value the value to be set.
|
||||
*/
|
||||
public Shader setUniformFloatVariable(String name, float value){
|
||||
return setUniformFloatVariable(name, new float[]{value});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Shader setUniformFloatVariable(String name,
|
||||
float v0, float v1){
|
||||
return setUniformFloatVariable(name, new float[]{v0, v1});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Shader setUniformFloatVariable(String name,
|
||||
float v0, float v1, float v2){
|
||||
return setUniformFloatVariable(name, new float[]{v0, v1, v2});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Shader setUniformFloatVariable(String name,
|
||||
float v0, float v1,
|
||||
float v2, float v3){
|
||||
return setUniformFloatVariable(name, new float[]{v0, v1, v2, v3});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Shader setUniformFloatVariable(String name, float[] values){
|
||||
ShaderVariable var = vars.get(name);
|
||||
if(var==null){
|
||||
printError(name);
|
||||
}else{
|
||||
var.setUniformValue(values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TODO implement using ShaderVariable
|
||||
//TODO Test
|
||||
public Shader setUniformMatrix(String name,
|
||||
boolean transpose,
|
||||
float[][] matrix){
|
||||
//Convert matrix format
|
||||
FloatBuffer matBuffer = matrixPrepare(matrix);
|
||||
|
||||
//Get uniform location
|
||||
int location = GL20.glGetUniformLocation(programID, name);
|
||||
printError(name);
|
||||
|
||||
//determine correct matrixSetter
|
||||
switch(matrix.length){
|
||||
case 2: GL20.glUniformMatrix2(location, transpose, matBuffer);
|
||||
break;
|
||||
case 3: GL20.glUniformMatrix3(location, transpose, matBuffer);
|
||||
break;
|
||||
case 4: GL20.glUniformMatrix4(location, transpose, matBuffer);
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private FloatBuffer matrixPrepare(float[][] matrix){
|
||||
//Check argument validity
|
||||
if(matrix==null){
|
||||
throw new IllegalArgumentException("The matrix may not be null");
|
||||
}
|
||||
int row = matrix.length;
|
||||
if(row<2){
|
||||
throw new IllegalArgumentException("The matrix must have at least 2 rows.");
|
||||
}
|
||||
int col = matrix[0].length;
|
||||
if(col!=row){
|
||||
throw new IllegalArgumentException("The matrix must have an equal number of rows and columns.");
|
||||
}
|
||||
float[] unrolled = new float[row*col];
|
||||
|
||||
for(int i=0;i<row;i++){
|
||||
for(int j=0;j<col;j++){
|
||||
unrolled[i*col+j] = matrix[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
//TODO FloatBuffer creation here is probably broken
|
||||
return FloatBuffer.wrap(unrolled);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void printError(String varName){
|
||||
System.err.printf(ERR_LOCATION, varName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the shader compiled successfully.</br>
|
||||
* @param shaderID
|
||||
* @return true if the shader compiled successfully.</br>
|
||||
*/
|
||||
private boolean compiledSuccessfully(int shaderID){
|
||||
return GL20.glGetShader(shaderID, GL20.GL_COMPILE_STATUS)==GL11.GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the shader program linked successfully.</br>
|
||||
* @return true if the shader program linked successfully.</br>
|
||||
*/
|
||||
private boolean linkedSuccessfully(){
|
||||
int test = GL20.glGetShader(programID, GL20.GL_LINK_STATUS);
|
||||
return true;
|
||||
// return GL20.glGetShader(programID, GL20.GL_LINK_STATUS)==GL11.GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String getShaderInfoLog(int shaderID){
|
||||
return GL20.glGetShaderInfoLog(shaderID, logging).trim();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String getProgramInfoLog(){
|
||||
return GL20.glGetProgramInfoLog(programID, logging).trim();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void scrapeVariables(String varLine){
|
||||
ShaderVariable.Qualifier qualifier = null;
|
||||
ShaderVariable.Type type = null;
|
||||
String name = "";
|
||||
int vecSize = 1; // if a vector the
|
||||
int size = 1; //If array size of array
|
||||
|
||||
String str;
|
||||
Scanner scanner = new Scanner(varLine);
|
||||
scanner.useDelimiter("[\\s,]++");
|
||||
|
||||
//Determine qualifier
|
||||
qualifier = ShaderVariable.Qualifier.fromString(scanner.next());
|
||||
|
||||
//Determine type
|
||||
str = scanner.next();
|
||||
if(str.equals("float")){
|
||||
type = ShaderVariable.Type.FLOAT;
|
||||
}else if(str.matches("[u]?int|sampler[123]D")){
|
||||
type = ShaderVariable.Type.INTEGER;
|
||||
}else if(str.equals("bool")){
|
||||
type = ShaderVariable.Type.BOOLEAN;
|
||||
}else if(str.matches("[bdiu]?vec[234]")){
|
||||
char c = str.charAt(0);
|
||||
switch(c){
|
||||
case 'b':
|
||||
type = ShaderVariable.Type.BOOLEAN; break;
|
||||
case 'd':
|
||||
type = ShaderVariable.Type.DOUBLE; break;
|
||||
case 'i':
|
||||
case 'u':
|
||||
type = ShaderVariable.Type.INTEGER; break;
|
||||
case 'v':
|
||||
type = ShaderVariable.Type.FLOAT; break;
|
||||
}
|
||||
|
||||
str = str.substring(str.length()-1);
|
||||
vecSize = Integer.parseInt(str);
|
||||
}
|
||||
|
||||
|
||||
//Determine variable names
|
||||
while(scanner.hasNext("[\\w_]+[\\w\\d_]*(\\[\\d+\\])?")){
|
||||
name = scanner.next();
|
||||
if(name.contains("]")){
|
||||
String sub = name.substring(name.indexOf('[')+1, name.length()-1);
|
||||
size = Integer.parseInt(sub);
|
||||
name = name.substring(0, name.indexOf('[')).trim();
|
||||
}
|
||||
|
||||
ShaderVariable var =
|
||||
new ShaderVariable(programID,
|
||||
name, qualifier, type, vecSize, size);
|
||||
vars.put(var.name, var);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void scanSource(String filename){
|
||||
Scanner scanner = new Scanner(ResourceLoader.getResourceAsStream(filename));
|
||||
scanner.useDelimiter(";|\\{|\\}");
|
||||
while(scanner.hasNext()){
|
||||
while(scanner.hasNext("\\s*?(uniform|attribute|varying).*")){
|
||||
scrapeVariables(scanner.next().trim());
|
||||
}
|
||||
scanner.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
197
src/shader/ShaderManagerImpl.java
Executable file
197
src/shader/ShaderManagerImpl.java
Executable file
@@ -0,0 +1,197 @@
|
||||
package shader;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
/**
|
||||
* A simple class used to prevent duplicate shaders from
|
||||
* being loaded and compiled onto the video card.</br>
|
||||
* @author Chronocide (Jeremy Klix)
|
||||
*/
|
||||
class ShaderResourceManagerImpl implements ShaderResourceManager{
|
||||
private static final ShaderResourceManager SRM = new ShaderResourceManagerImpl();
|
||||
|
||||
|
||||
private Map<String, Integer> shaderMap = new HashMap<String, Integer>();
|
||||
private Map<Integer, Set<Integer>> shaderToPrograms = new HashMap<Integer, Set<Integer>>(); //for every shader lists all the programs that use it
|
||||
private Map<Integer, Set<Integer>> programToShaders = new HashMap<Integer, Set<Integer>>(); //for every program lists the shaders it uses
|
||||
|
||||
|
||||
//Constructor
|
||||
private ShaderResourceManagerImpl(){
|
||||
//Private Constructor to prevent external extension
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Factory Method
|
||||
static ShaderResourceManager getSRM(){
|
||||
return SRM;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fetches a shader id for a given fragment shader, generating
|
||||
* a new id if necessary.</br>
|
||||
*
|
||||
* @param fragmentFileName the fragment shader to fetch an id for.
|
||||
* @returns shaderID for a given fragment shader.</br>
|
||||
*/
|
||||
public int getFragementShaderID(String fragmentFileName)throws SlickException{
|
||||
Integer id = shaderMap.get(fragmentFileName);
|
||||
if(id==null){
|
||||
id = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
|
||||
shaderMap.put(fragmentFileName, id);
|
||||
|
||||
GL20.glShaderSource(id, getProgramCode(fragmentFileName));
|
||||
GL20.glCompileShader(id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fetches a shader id for a given vertex shader, generating
|
||||
* a new id if necessary.</br>
|
||||
*
|
||||
* @param vertexFileName the vertex shader to fetch an id for.
|
||||
* @returns shaderID for a given vertex shader.</br>
|
||||
*/
|
||||
public int getVertexShaderID(String vertexFileName)throws SlickException{
|
||||
Integer id = shaderMap.get(vertexFileName);
|
||||
if(id==null){
|
||||
id = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
|
||||
shaderMap.put(vertexFileName, id);
|
||||
|
||||
GL20.glShaderSource(id, getProgramCode(vertexFileName));
|
||||
GL20.glCompileShader(id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Link a shader that the shader program depends on to operate.</br>
|
||||
* @param programID
|
||||
* @param shaderID
|
||||
*/
|
||||
public void createProgramShaderDependancy(int programID,
|
||||
int shaderID){
|
||||
if(!shaderMap.containsValue(shaderID)){
|
||||
throw new IllegalArgumentException("Cannot link a shader " +
|
||||
"id that does not exist.");
|
||||
}
|
||||
|
||||
//Add Shader to list of shaders used by program
|
||||
Set<Integer> shaders = programToShaders.get(programID);
|
||||
if(shaders==null){
|
||||
shaders = new HashSet<Integer>();
|
||||
programToShaders.put(programID, shaders);
|
||||
}
|
||||
shaders.add(shaderID);
|
||||
|
||||
//Add program to list of programs used by Shader
|
||||
Set<Integer> programs = shaderToPrograms.get(shaderID);
|
||||
if(programs==null){
|
||||
programs = new HashSet<Integer>();
|
||||
shaderToPrograms.put(shaderID, programs);
|
||||
}
|
||||
programs.add(programID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void createProgramShaderDependancies(int programID,
|
||||
Iterable<Integer> ids){
|
||||
for(int id : ids){
|
||||
createProgramShaderDependancy(programID, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removes the program.</br>
|
||||
* After calling this method the program specified will no longer
|
||||
* be loaded, nor will any shaders that were still in use only by
|
||||
* only that program.</br>
|
||||
*
|
||||
* @params programID the ID of the program to remove.
|
||||
*/
|
||||
//This is a rather inefficient implementation however, it need not
|
||||
//be fast and this representation is very simple to follow and
|
||||
//debug.
|
||||
public void removeProgram(int programID){
|
||||
Set<Integer> shaders = programToShaders.get(programID);
|
||||
if(shaders==null){
|
||||
throw new IllegalArgumentException("The programID " +
|
||||
programID +
|
||||
"does not exist");
|
||||
}
|
||||
|
||||
//detach Shaders from program
|
||||
for(int id : shaders){
|
||||
GL20.glDetachShader(programID, id);
|
||||
}
|
||||
|
||||
//Delete unused shaders
|
||||
for(int id : shaders){
|
||||
Set<Integer> progs = shaderToPrograms.get(id);
|
||||
progs.remove(programID);
|
||||
if(progs.isEmpty()){
|
||||
GL20.glDeleteShader(id);
|
||||
shaderToPrograms.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
//Delete Program
|
||||
GL20.glDeleteProgram(programID);
|
||||
programToShaders.remove(programID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the program code from the file <tt>filename</tt> and puts
|
||||
* it into a <tt>ByteBuffer</tt>.</br>
|
||||
* @param filename the full name of the file.
|
||||
* @return a ByteBuffer containing the program code.
|
||||
* @throws SlickException
|
||||
*/
|
||||
private ByteBuffer getProgramCode(String filename)throws SlickException{
|
||||
InputStream fileInputStream = null;
|
||||
byte[] shaderCode = null;
|
||||
|
||||
fileInputStream = ResourceLoader.getResourceAsStream(filename);
|
||||
DataInputStream dataStream = new DataInputStream(fileInputStream);
|
||||
try{
|
||||
dataStream.readFully(shaderCode = new byte[fileInputStream.available()]);
|
||||
fileInputStream.close();
|
||||
dataStream.close();
|
||||
}catch (IOException e) {
|
||||
throw new SlickException(e.getMessage());
|
||||
}
|
||||
|
||||
ByteBuffer shaderPro = BufferUtils.createByteBuffer(shaderCode.length);
|
||||
|
||||
shaderPro.put(shaderCode);
|
||||
shaderPro.flip();
|
||||
|
||||
return shaderPro;
|
||||
}
|
||||
|
||||
}
|
||||
25
src/shader/ShaderResourceManager.java
Executable file
25
src/shader/ShaderResourceManager.java
Executable file
@@ -0,0 +1,25 @@
|
||||
package shader;
|
||||
|
||||
import org.newdawn.slick.SlickException;
|
||||
|
||||
|
||||
/**
|
||||
* Simply interface for component that manages shaders source files.
|
||||
* @author Chronocide (Jeremy Klix)
|
||||
*
|
||||
*/
|
||||
public interface ShaderResourceManager{
|
||||
|
||||
int getFragementShaderID(String fragmentFileName)throws SlickException;
|
||||
|
||||
int getVertexShaderID(String vertexFileName)throws SlickException;
|
||||
|
||||
/**
|
||||
* Link a shader that the shader program depends on to operate.</br>
|
||||
* @param programID
|
||||
* @param shaderID
|
||||
*/
|
||||
void createProgramShaderDependancy(int programID, int shaderID);
|
||||
|
||||
void removeProgram(int programID);
|
||||
}
|
||||
234
src/shader/ShaderVariable.java
Executable file
234
src/shader/ShaderVariable.java
Executable file
@@ -0,0 +1,234 @@
|
||||
package shader;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
|
||||
/**
|
||||
* Class used to keep track of variables associated with this
|
||||
* shader.</br>
|
||||
*
|
||||
* @author Chronocide (Jeremy Klix)
|
||||
*
|
||||
*/
|
||||
class ShaderVariable{
|
||||
public enum Qualifier{
|
||||
ATTRIBUTE("attribute"), UNIFORM("uniform"), VARYING("varying");
|
||||
|
||||
private static final Map<String, ShaderVariable.Qualifier> stringToEnum =
|
||||
new HashMap<String, ShaderVariable.Qualifier>();
|
||||
|
||||
static{
|
||||
for(ShaderVariable.Qualifier qual : values()){
|
||||
stringToEnum.put(qual.toString(),qual);
|
||||
}
|
||||
}
|
||||
|
||||
private String name;
|
||||
|
||||
|
||||
Qualifier(String name){
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public static ShaderVariable.Qualifier fromString(String token){
|
||||
return stringToEnum.get(token);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Type{
|
||||
BOOLEAN("boolean"), DOUBLE("double"), FLOAT("float"),
|
||||
INTEGER("integer");
|
||||
|
||||
private String name;
|
||||
|
||||
Type(String name){
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final String TYPE_WARN =
|
||||
"Warning!\nProblem setting %s variable. " +
|
||||
"Expected type %s but got type %s instead.\n";
|
||||
private static final String QUAL_WARN =
|
||||
"Warning!\nProblem setting %s variable. " +
|
||||
"Expected qualifier %s but got %s instead.\n";
|
||||
|
||||
private ShaderVariable.Qualifier qualifier = null;
|
||||
private ShaderVariable.Type type = null;
|
||||
private int vecSize; //size of vector
|
||||
private int size; //size of array non arrays are size 1
|
||||
private int programID;
|
||||
|
||||
|
||||
private int location = -1;
|
||||
|
||||
/**Set true if GLSL has removed this unused variable*/
|
||||
private boolean isCulled = false;
|
||||
String name = "";
|
||||
|
||||
ShaderVariable(int programID, String name,
|
||||
ShaderVariable.Qualifier qual,
|
||||
ShaderVariable.Type type,
|
||||
int vecSize,
|
||||
int size){
|
||||
this.programID = programID;
|
||||
this.name = name;
|
||||
|
||||
this.qualifier = qual;
|
||||
this.type = type;
|
||||
if(vecSize<1){
|
||||
throw new IllegalArgumentException("size of elements must be greater than 0");
|
||||
}
|
||||
this.vecSize = vecSize;
|
||||
if(size<1){
|
||||
throw new IllegalArgumentException("number of elements must be greater than 0");
|
||||
}
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String toString(){
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean equals(Object obj){
|
||||
if(obj instanceof ShaderVariable){
|
||||
return this.toString().equals(obj.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int hashCode(){
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
|
||||
//TODO TEST
|
||||
void setUniformValue(boolean[] vals){
|
||||
if(this.type!=Type.BOOLEAN){
|
||||
System.err.printf(TYPE_WARN, this.name, this.type, Type.BOOLEAN);
|
||||
}
|
||||
if(this.qualifier!=Qualifier.UNIFORM){
|
||||
System.err.printf(QUAL_WARN, this.name, this.qualifier, Qualifier.UNIFORM);
|
||||
}
|
||||
if(vals.length!=vecSize){
|
||||
throw new AssertionError("Incorrect number of arguments.");
|
||||
}
|
||||
|
||||
//No GL methods to set boolean uniforms exist
|
||||
if(location==-1){
|
||||
CharSequence param = new StringBuffer(name);
|
||||
location = GL20.glGetUniformLocation(programID, param);
|
||||
locationCheck();
|
||||
}
|
||||
|
||||
IntBuffer fb = BufferUtils.createIntBuffer(vals.length);
|
||||
for(boolean b : vals){
|
||||
fb.put(b? 1 : 0);
|
||||
}
|
||||
fb.flip();
|
||||
switch(vecSize){
|
||||
case 1: GL20.glUniform1(location, fb); break;
|
||||
case 2: GL20.glUniform2(location, fb); break;
|
||||
case 3: GL20.glUniform3(location, fb); break;
|
||||
case 4: GL20.glUniform4(location, fb); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setUniformValue(float[] vals){
|
||||
if(this.type!=Type.FLOAT){
|
||||
System.err.printf(TYPE_WARN, this.name, this.type, Type.FLOAT);
|
||||
}
|
||||
if(this.qualifier!=Qualifier.UNIFORM){
|
||||
System.err.printf(QUAL_WARN, this.name, this.qualifier, Qualifier.UNIFORM);
|
||||
}
|
||||
if(vals.length!=vecSize*size){
|
||||
throw new AssertionError("Incorrect number of values.\n" +
|
||||
"Expected " + vecSize*size + " vlaues but got " +
|
||||
vals.length + " values instead.");
|
||||
}
|
||||
|
||||
if(location==-1){
|
||||
CharSequence param = new StringBuffer(name);
|
||||
location = GL20.glGetUniformLocation(programID, param);
|
||||
locationCheck();
|
||||
}
|
||||
|
||||
FloatBuffer fb = BufferUtils.createFloatBuffer(vals.length);
|
||||
fb.put(vals);
|
||||
fb.flip();
|
||||
switch(vecSize){
|
||||
case 1: GL20.glUniform1(location, fb); break;
|
||||
case 2: GL20.glUniform2(location, fb); break;
|
||||
case 3: GL20.glUniform3(location, fb); break;
|
||||
case 4: GL20.glUniform4(location, fb); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setUniformValue(int[] vals){
|
||||
if(this.type!=Type.INTEGER){
|
||||
System.err.printf(TYPE_WARN, this.type, Type.INTEGER);
|
||||
}
|
||||
if(this.qualifier!=Qualifier.UNIFORM){
|
||||
System.err.printf(QUAL_WARN, this.name, this.qualifier, Qualifier.UNIFORM);
|
||||
}
|
||||
if(vals.length!=vecSize*size){
|
||||
throw new AssertionError("Incorrect number of values.\n" +
|
||||
"Expected " + vecSize*size + " vlaues but got " +
|
||||
vals.length + " values instead.");
|
||||
}
|
||||
|
||||
if(location==-1){
|
||||
CharSequence param = new StringBuffer(name);
|
||||
location = GL20.glGetUniformLocation(programID, param);
|
||||
locationCheck();
|
||||
}
|
||||
|
||||
IntBuffer fb = BufferUtils.createIntBuffer(vals.length);
|
||||
fb.put(vals);
|
||||
fb.flip();
|
||||
switch(vecSize){
|
||||
case 1: GL20.glUniform1(location, fb); break;
|
||||
case 2: GL20.glUniform2(location, fb); break;
|
||||
case 3: GL20.glUniform3(location, fb); break;
|
||||
case 4: GL20.glUniform4(location, fb); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void locationCheck(){
|
||||
if(location==-1 && !isCulled){
|
||||
System.err.println("Location for variable " + name +
|
||||
"could not be found.\nGLSL may remove " +
|
||||
"any vairable that does not contribute " +
|
||||
"to an output. Check and ensure " +
|
||||
"that " + name + "is an active variable.\n");
|
||||
isCulled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user