Commit 0deeecf2 authored by Lockhead's avatar Lockhead

Fix and improve lighting system

parent 62b20791
Pipeline #58489219 (#46) passed with stage
in 1 minute and 32 seconds
......@@ -506,6 +506,7 @@ public class GameScreen implements Screen {
new Keybind(code -> {if (!look) player.setState(EntityPlayer.ActionState.INSPECT);}, VK_F),
new Keybind(code -> {if (!look) player.setState(EntityPlayer.ActionState.TALK);}, VK_T),
new Keybind(code -> {if (!look) player.setState(EntityPlayer.ActionState.TILE_CLOSE);}, VK_C),
new Keybind(code -> {if (!look) player.setState(EntityPlayer.ActionState.PLACE_LAMP);}, VK_Q),
// new Keybind(code -> {if (!look) player.setState(EntityPlayer.ActionState.TILE_PLACE);}, VK_P),
new Keybind(code -> {if (!look) player.setState(EntityPlayer.ActionState.ITEM_DROP);}, VK_D),
new Keybind(code -> {if (!look) player.setState(EntityPlayer.ActionState.TILE_GRAB);}, VK_G),
......
package nl.lockhead.oddfolk.game.screens;
import asciiPanel.AsciiPanel;
import nl.lockhead.oddfolk.Main;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
public class NotificationScreen extends PauseScreen {
private String description;
private boolean closeOnInput;
private long timeAlive;
Timer timer;
public NotificationScreen(String title, String description, int left, int top, int width, int height) {
super(left, top, width, height);
......@@ -15,13 +21,31 @@ public class NotificationScreen extends PauseScreen {
this.top = top;
this.width = width;
this.height = height;
onOpen();
this.title = title;
this.description = description;
ActionListener taskPerformer = evt -> {
if (GameScreen.getInstance() == null || GameScreen.getPlayer().getRadius() <= 0) {
timer.stop();
return;
}
else
GameScreen.getPlayer().setRadius(GameScreen.getPlayer().getRadius() - 1);
GameScreen.getWorld().onTick();
};
timer = new javax.swing.Timer(200, taskPerformer);
timer.start();
onOpen();
}
public NotificationScreen(String title, String description) {
this(title, description, 0, 5, Math.max(title.length() + 2, description.length() + 2), 5);
left = Main.mainWindow.getPanel().getWidthInCharacters() / 2 - width / 2;
}
@Override
public Screen onInput(KeyEvent event) {
if (closeOnInput)
onClose();
return this;
}
......@@ -42,4 +66,12 @@ public class NotificationScreen extends PauseScreen {
public String getDescription() {
return description;
}
public boolean isCloseOnInput() {
return closeOnInput;
}
public void setCloseOnInput(boolean closeOnInput) {
this.closeOnInput = closeOnInput;
}
}
......@@ -2,6 +2,7 @@ package nl.lockhead.oddfolk.game.utilities;
import javax.vecmath.Point2d;
import javax.vecmath.Point2i;
import java.awt.*;
import static java.lang.Math.*;
public class Tools {
......@@ -51,6 +52,16 @@ public class Tools {
return (float) toDegrees(atan2(y1 - y2, x2 - x1));
}
public static Color getColorFrom(String s) {
Color c;
if (s.matches("\\d+,\\d+,\\d+")) {
String[] ss = s.split(",");
c = new Color(Integer.parseInt(ss[0]), Integer.parseInt(ss[1]), Integer.parseInt(ss[2]));
}
else c = Color.getColor(s);
return c;
}
public static float toFahrenheit(float tempC) {
return tempC * 9 / 5 + 32;
}
......
package nl.lockhead.oddfolk.game.utilities.fov;
import javax.vecmath.Point2i;
import javax.vecmath.Point3i;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
......@@ -9,11 +10,6 @@ public class Line {
private List<Point2i> points;
// public Line(int x1, int y1, int z1, int x2, int y2, int z2) {
// points = line3D(x1, y1, z1, x2, y2, z2);
// }
public Line(int x1, int y1, int x2, int y2) {
points = line2D(x1, y1, x2, y2);
}
......@@ -46,109 +42,109 @@ public class Line {
}
return points;
}
//
// /**
// * Generates a 3D Bresenham line between the given coordinates.
// *
// * @param startx the x coordinate of the starting point
// * @param starty the y coordinate of the starting point
// * @param startz the z coordinate of the starting point
// * @param endx the x coordinate of the starting point
// * @param endy the y coordinate of the starting point
// * @param endz the z coordinate of the starting point
// * @return
// */
// public static List<Point2i> line3D(int startx, int starty, int startz, int endx, int endy, int endz) {
// List<Point2i> result = new ArrayList<>();
//
// int dx = endx - startx;
// int dy = endy - starty;
// int dz = endz - startz;
//
// int ax = Math.abs(dx) << 1;
// int ay = Math.abs(dy) << 1;
// int az = Math.abs(dz) << 1;
//
// int signx = (int) Math.signum(dx);
// int signy = (int) Math.signum(dy);
// int signz = (int) Math.signum(dz);
//
// int x = startx;
// int y = starty;
// int z = startz;
//
// int deltax, deltay, deltaz;
// if (ax >= Math.max(ay, az)) /* x dominant */ {
// deltay = ay - (ax >> 1);
// deltaz = az - (ax >> 1);
// while (true) {
// result.add(new Point3i(x, y, z));
// if (x == endx) {
// return result;
// }
//
// if (deltay >= 0) {
// y += signy;
// deltay -= ax;
// }
//
// if (deltaz >= 0) {
// z += signz;
// deltaz -= ax;
// }
//
// x += signx;
// deltay += ay;
// deltaz += az;
// }
// } else if (ay >= Math.max(ax, az)) /* y dominant */ {
// deltax = ax - (ay >> 1);
// deltaz = az - (ay >> 1);
// while (true) {
// result.add(new Point3i(x, y, z));
// if (y == endy) {
// return result;
// }
//
// if (deltax >= 0) {
// x += signx;
// deltax -= ay;
// }
//
// if (deltaz >= 0) {
// z += signz;
// deltaz -= ay;
// }
//
// y += signy;
// deltax += ax;
// deltaz += az;
// }
// } else {
// deltax = ax - (az >> 1);
// deltay = ay - (az >> 1);
// while (true) {
// result.add(new Point3i(x, y, z));
// if (z == endz) {
// return result;
// }
//
// if (deltax >= 0) {
// x += signx;
// deltax -= az;
// }
//
// if (deltay >= 0) {
// y += signy;
// deltay -= az;
// }
//
// z += signz;
// deltax += ax;
// deltay += ay;
// }
// }
// }
/**
* Generates a 3D Bresenham line between the given coordinates.
*
* @param startx the x coordinate of the starting point
* @param starty the y coordinate of the starting point
* @param startz the z coordinate of the starting point
* @param endx the x coordinate of the starting point
* @param endy the y coordinate of the starting point
* @param endz the z coordinate of the starting point
* @return
*/
public static List<Point3i> line3D(int startx, int starty, int startz, int endx, int endy, int endz) {
List<Point3i> result = new ArrayList<>();
int dx = endx - startx;
int dy = endy - starty;
int dz = endz - startz;
int ax = Math.abs(dx) << 1;
int ay = Math.abs(dy) << 1;
int az = Math.abs(dz) << 1;
int signx = (int) Math.signum(dx);
int signy = (int) Math.signum(dy);
int signz = (int) Math.signum(dz);
int x = startx;
int y = starty;
int z = startz;
int deltax, deltay, deltaz;
if (ax >= Math.max(ay, az)) /* x dominant */ {
deltay = ay - (ax >> 1);
deltaz = az - (ax >> 1);
while (true) {
result.add(new Point3i(x, y, z));
if (x == endx) {
return result;
}
if (deltay >= 0) {
y += signy;
deltay -= ax;
}
if (deltaz >= 0) {
z += signz;
deltaz -= ax;
}
x += signx;
deltay += ay;
deltaz += az;
}
} else if (ay >= Math.max(ax, az)) /* y dominant */ {
deltax = ax - (ay >> 1);
deltaz = az - (ay >> 1);
while (true) {
result.add(new Point3i(x, y, z));
if (y == endy) {
return result;
}
if (deltax >= 0) {
x += signx;
deltax -= ay;
}
if (deltaz >= 0) {
z += signz;
deltaz -= ay;
}
y += signy;
deltax += ax;
deltaz += az;
}
} else {
deltax = ax - (az >> 1);
deltay = ay - (az >> 1);
while (true) {
result.add(new Point3i(x, y, z));
if (z == endz) {
return result;
}
if (deltax >= 0) {
x += signx;
deltax -= az;
}
if (deltay >= 0) {
y += signy;
deltay -= az;
}
z += signz;
deltax += ax;
deltay += ay;
}
}
}
public List<Point2i> getPoints() {
return points;
......
......@@ -3,6 +3,7 @@ package nl.lockhead.oddfolk.game.world;
import nl.lockhead.oddfolk.Main;
import nl.lockhead.oddfolk.game.screens.AttackScreen;
import nl.lockhead.oddfolk.game.screens.GameScreen;
import nl.lockhead.oddfolk.game.utilities.Tools;
import nl.lockhead.oddfolk.game.world.attributes.AttributeGeneric;
import nl.lockhead.oddfolk.game.world.entities.Bodypart;
import nl.lockhead.oddfolk.game.world.entities.Entity;
......@@ -11,17 +12,18 @@ import nl.lockhead.oddfolk.game.world.entities.ai.AIEntity;
import nl.lockhead.oddfolk.game.world.interaction.Interact;
import nl.lockhead.oddfolk.game.world.items.impl.Item;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.StringJoiner;
import static nl.lockhead.oddfolk.game.world.Flag.EventType.CONSUME;
import static nl.lockhead.oddfolk.game.world.Flag.EventType.USE;
import static nl.lockhead.oddfolk.game.world.Flag.EventType.*;
import static nl.lockhead.oddfolk.game.world.Sound.Type.OTHER;
public enum Flag {
DEBUG("debug -- you aren't supposed to see this ingame",
(o, e, d) -> System.out.printf("%s:%s\n", o, e)),
DEBUG_FLAG("debug -- you aren't supposed to see this ingame",
(o, e, d) -> System.out.printf("%s:%s\n", o, e), DEBUG),
FIREARM("shoot",
(o, e, d) -> {
......@@ -75,6 +77,35 @@ public enum Flag {
});
}, USE),
LUMINOUS("emit light",
(o, e, d) -> {
World w = o.getWorld() == null ? GameScreen.getWorld() : o.getWorld();
if (w == null)
return;
if (e == SPAWN || e == FLAG_ADD) {
double strength = 1;
Color c = null;
if (d.length > 1) {
if (d[0] != null)
strength = (double) d[0];
if (d[1] != null)
c = Tools.getColorFrom(String.valueOf(d[1]));
}
for (Light l: w.getLights()) {
if (l.getLocation().equals(o.getLocation())) {
return;
}
}
w.addLight(new Light(o.getLocation(), strength, c));
}
else if (e == DESPAWN || e == FLAG_REMOVE) {
new ArrayList<>(w.getLights()).forEach(l -> {
if (l.getLocation().equals(o.getLocation()))
w.getLights().remove(l);
});
}
}, SPAWN, FLAG_ADD, DESPAWN, FLAG_REMOVE),
// DEBUG2("you aren't supposed to see this",
// (o, e, d) -> System.out.printf("%s:%s\n", o, e)),
;
......@@ -123,7 +154,7 @@ public enum Flag {
}
public enum EventType {
DEBUG, TICK, DROP, FALL, USE, CONSUME, OTHER;
DEBUG, TICK, FLAG_ADD, FLAG_REMOVE, SPAWN, DESPAWN, DROP, FALL, USE, CONSUME, OTHER;
public String toString() {
return name().toLowerCase();
......
......@@ -4,42 +4,16 @@ import java.awt.*;
public class Light {
private int x, y, z;
private Location location;
private double strength;
private Color color;
public Light(int x, int y, int z, double strength, Color color) {
this.x = x;
this.y = y;
this.z = z;
public Light(Location location, double strength, Color color) {
this.location = location;
this.strength = strength;
this.color = color;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
public double getStrength() {
return strength;
}
......@@ -55,4 +29,12 @@ public class Light {
public void setColor(Color color) {
this.color = color;
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
}
......@@ -156,8 +156,6 @@ public class Location implements Serializable {
}
public World getWorld() {
if (world == null)
System.out.println("World is null!");
return world;
}
......
......@@ -18,7 +18,7 @@ import nl.lockhead.oddfolk.game.world.particles.ParticleGenerator;
import nl.lockhead.oddfolk.game.world.tiles.Tile;
import nl.lockhead.oddfolk.game.world.tiles.TileWall;
import javax.vecmath.Point2i;
import javax.vecmath.Point3i;
import java.awt.event.KeyEvent;
import java.io.Serializable;
import java.util.ArrayList;
......@@ -157,9 +157,11 @@ public class World implements Serializable {
return;
if (coordX >= width || coordY >= height || coordZ >= zHeight)
return;
// if (tile == null)
// return;
if (tiles[coordX][coordY][coordZ] != null)
tiles[coordX][coordY][coordZ].despawn();
tiles[coordX][coordY][coordZ] = tile;
if (tile != null)
tile.getLocation().teleport(coordX, coordY, coordZ);
updateWallAt(tiles, coordX, coordY, coordZ, tile);
updateLights();
}
......@@ -503,16 +505,12 @@ public class World implements Serializable {
public boolean canSee(int x0, int y0, int z0, int x, int y, int z, int radius) {
// if (z != z0)
// return false;
if (((x - x0) * (x - x0)) +
((y - y0) * (y - y0)) >
radius * radius)
return false;
for (Point2i p: new Line(x0, y0, x, y).getPoints()) {
for (Point3i p: Line.line3D(x, y, z, x0, y0, z0)) {
if (radius <= 0)
return false;
radius--;
if (p.x == x && p.y == y)
break;
continue;
Tile tile = getTile(p.x, p.y, z);
if (tile == null)
continue;
......@@ -533,10 +531,12 @@ public class World implements Serializable {
}
public void updateLight(Light l) {
if (!l.getLocation().getWorld().equals(this))
return;
int x, y, z, radius;
x = l.getX();
y = l.getY();
z = l.getZ();
x = (int)l.getLocation().getX();
y = (int)l.getLocation().getY();
z = (int)l.getLocation().getZ();
radius = (int) (l.getStrength() * 10);
try {
for(int xx = x - radius; xx < x + radius; ++xx) {
......@@ -550,8 +550,9 @@ public class World implements Serializable {
+ Math.abs((yy - y) * (yy - y))
+ Math.abs((zz - z) * (zz - z))) / (float)radius)) * l.getStrength();
light = RareMath.roundToFraction(light, 3);
if (canSee(xx, yy, zz, x, y, z, radius))
if (canSee(xx, yy, zz, x, y, z, radius)) {
t.setLight(light + t.getLight());
}
}
}
}
......@@ -677,4 +678,8 @@ public class World implements Serializable {
return false;
return ((World) obj).id == id;
}
public List<Light> getLights() {
return lights;
}
}
......@@ -25,6 +25,7 @@ import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.*;
import static nl.lockhead.oddfolk.game.world.Flag.EventType.*;
import static nl.lockhead.oddfolk.game.world.attributes.AttributeState.Type.*;
public class WorldObject implements Serializable {
......@@ -74,10 +75,21 @@ public class WorldObject implements Serializable {
private WorldObject() {
instanceId = instanceIdCounter++;
spawn();
}
public void spawn() {
id = getClass().getSimpleName()
.replaceAll("Tile", "t_")
.replaceAll("Item", "i_")
.toLowerCase();
flags.stream().filter(flag -> flag.hasEvent(SPAWN))
.forEachOrdered(flag -> flag.getOnEvent().on(this, SPAWN));
}
public void despawn() {
flags.stream().filter(flag -> flag.hasEvent(DESPAWN))
.forEachOrdered(flag -> flag.getOnEvent().on(this, DESPAWN));
}
public static void loadObjects(File directory) {
......@@ -494,10 +506,23 @@ public class WorldObject implements Serializable {
return flags;
}
public void addFlags(Flag... flags) {
public void addFlag(Flag... flags) {
Arrays.stream(flags).forEach(f -> {
if (!WorldObject.this.flags.contains(f))
if (!WorldObject.this.flags.contains(f)) {
if (f.hasEvent(FLAG_ADD))
f.getOnEvent().on(this, FLAG_ADD);
WorldObject.this.flags.add(f);
}
});
}
public void removeFlag(Flag... flags) {
Arrays.stream(flags).forEach(f -> {
if (WorldObject.this.flags.contains(f)) {
if (f.hasEvent(FLAG_REMOVE))
f.getOnEvent().on(this, FLAG_REMOVE);
WorldObject.this.flags.remove(f);
}
});
}
......
......@@ -7,6 +7,7 @@ import nl.lockhead.oddfolk.game.world.WorldObject;
import nl.lockhead.oddfolk.game.world.attributes.AttributeItemLayingOn;
import nl.lockhead.oddfolk.game.world.items.impl.Item;
import nl.lockhead.oddfolk.game.world.tiles.Tile;
import nl.lockhead.oddfolk.game.world.tiles.furniture.TileLamp;
import java.awt.*;
......@@ -17,7 +18,7 @@ public class EntityPlayer extends EntityHuman {
private float renderRadius;
public enum ActionState {
MOVE, INSPECT, ATTACK_PRECISE, ATTACK_RANDOM, TILE_CLOSE, TILE_GRAB, ITEM_DROP, TALK
MOVE, INSPECT, ATTACK_PRECISE, ATTACK_RANDOM, TILE_CLOSE, TILE_GRAB, ITEM_DROP, TALK, PLACE_LAMP
}
private ActionState state = MOVE;
......@@ -48,12 +49,15 @@ public class EntityPlayer extends EntityHuman {
return;
this.state = state;
switch (state) {
case TILE_GRAB:
InfoScreen.addMessage("grab where?", Color.orange);
break;
case ITEM_DROP:
InfoScreen.addMessage("drop where?", Color.orange);
break;
case MOVE:
break;
case INSPECT:
InfoScreen.addMessage("inspect where?", Color.cyan);
break;
......@@ -69,6 +73,9 @@ public class EntityPlayer extends EntityHuman {
case TALK:
InfoScreen.addMessage("talk with whom?", Color.cyan);
break;
case PLACE_LAMP:
InfoScreen.addMessage("place lamp where?", Color.cyan);
break;
default:
color = Color.WHITE;
break;
......@@ -142,12 +149,11 @@ public class EntityPlayer extends EntityHuman {
else
InfoScreen.addMessage("nothing there", Color.yellow);
break;
// case DEBUG_WALLSPAWN:
// if (deltaX == 0 && deltaY == 0)
// l.getWorld().setTile(l.getX() + deltaX, l.getY() + deltaY, 0, new TileFloor());
// else
// l.getWorld().setTile(l.getX() + deltaX, l.getY() + deltaY, 1, new TileWall(TileWall.Type.WOOD));
// break;
case PLACE_LAMP:
if (deltaX != 0 || deltaY != 0)
l.getWorld().setTile((int)(l.getX() + deltaX), (int)(l.getY() + deltaY), (int)(l.getZ()), new TileLamp());
break;
case TILE_CLOSE:
if (t != null) {
t.getEntries().stream().filter(tt -> tt.getMessage().getText().toLowerCase().contains("close"))
......
......@@ -41,7 +41,7 @@ public class AIEntity implements Serializable {
public AIEntity(Entity entity) {
this.entity = entity;
messageTypes = List.of(GREETING, FAREWELL, AGREE, DISAGREE);
messageTypes = Arrays.asList(GREETING, FAREWELL, AGREE, DISAGREE);
this.context = new EntityContext(entity, this);
tree = NodeFactory.createTreeFor(entity.getSpecies().getType());
}
......
......@@ -50,7 +50,7 @@ public class Biome {
biomes.add(Importer.get().importBiome(file)));
if (biomes.isEmpty()) {
System.err.println("Biome folder was empty, adding defaults instead");