Entire project

This commit is contained in:
Gnarwhal 2024-08-07 04:58:07 +00:00
commit e1c6c794d4
Signed by: Gnarwhal
GPG key ID: 0989A73D8C421174
108 changed files with 2908 additions and 0 deletions

View file

@ -0,0 +1,65 @@
package com.gnarly.engine.display;
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class Camera {
private float width, height;
private Matrix4f projection;
private Vector3f position;
private float rotation;
public Camera(int width, int height) {
position = new Vector3f();
rotation = 0;
projection = new Matrix4f();
setDims(width, height);
}
public void setDims(float width, float height) {
this.width = width;
this.height = height;
projection.setOrtho(0, width, height, 0, 1, -1);
}
public void translate(int x, int y) {
position.sub(x, y, 0);
}
public void translate(Vector3f vector) {
position.sub(vector);
}
public void setPosition(int x, int y) {
position.set(x, y, 0);
}
public void setPosition(Vector3f vector) {
position.set(vector);
}
public void rotate(float angle) {
this.rotation -= angle;
}
public void setRotation(float angle) {
this.rotation = angle;
}
public float getWidth() {
return width;
}
public float getHeight() {
return height;
}
public Matrix4f getProjection() {
return projection;
}
public Matrix4f getView() {
return new Matrix4f().rotateZ(rotation).translate(position);
}
}

View file

@ -0,0 +1,65 @@
package com.gnarly.engine.display;
import java.nio.ByteBuffer;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL32.*;
public class Framebuffer {
private int fbo;
private int texture;
private int depth;
private int width, height;
public Framebuffer(int width, int height) {
this.width = width;
this.height = height;
fbo = createFrameBuffer();
texture = createTextureAttachment();
depth = createDepthBufferAttachment();
}
public void cleanUp() {
glDeleteFramebuffers(fbo);
glDeleteTextures(texture);
glDeleteRenderbuffers(depth);
}
public void bind() {
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, width, height);
}
public int getTexture() {
return texture;
}
private int createFrameBuffer() {
int frameBuffer = glGenFramebuffers();
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
return frameBuffer;
}
private int createTextureAttachment() {
int texture = glGenTextures();
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (ByteBuffer) null);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
return texture;
}
private int createDepthBufferAttachment() {
int depthBuffer = glGenRenderbuffers();
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
return depthBuffer;
}
}

View file

@ -0,0 +1,279 @@
package com.gnarly.engine.display;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR;
import static org.lwjgl.glfw.GLFW.GLFW_FALSE;
import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_CORE_PROFILE;
import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE;
import static org.lwjgl.glfw.GLFW.GLFW_PRESS;
import static org.lwjgl.glfw.GLFW.GLFW_RESIZABLE;
import static org.lwjgl.glfw.GLFW.GLFW_TRUE;
import static org.lwjgl.glfw.GLFW.glfwCreateWindow;
import static org.lwjgl.glfw.GLFW.glfwGetCursorPos;
import static org.lwjgl.glfw.GLFW.glfwGetKey;
import static org.lwjgl.glfw.GLFW.glfwGetMouseButton;
import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor;
import static org.lwjgl.glfw.GLFW.glfwGetVideoMode;
import static org.lwjgl.glfw.GLFW.glfwInit;
import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent;
import static org.lwjgl.glfw.GLFW.glfwPollEvents;
import static org.lwjgl.glfw.GLFW.glfwSetErrorCallback;
import static org.lwjgl.glfw.GLFW.glfwSetScrollCallback;
import static org.lwjgl.glfw.GLFW.glfwSetWindowPos;
import static org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose;
import static org.lwjgl.glfw.GLFW.glfwSetWindowSizeCallback;
import static org.lwjgl.glfw.GLFW.glfwSwapBuffers;
import static org.lwjgl.glfw.GLFW.glfwSwapInterval;
import static org.lwjgl.glfw.GLFW.glfwWindowHint;
import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose;
import static org.lwjgl.opengl.GL.createCapabilities;
import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glBlendFunc;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glClearColor;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glViewport;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.system.MemoryUtil.NULL;
import org.joml.Vector3f;
import org.lwjgl.glfw.GLFWCharCallback;
import org.lwjgl.glfw.GLFWCharModsCallback;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.glfw.GLFWScrollCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.glfw.GLFWWindowSizeCallback;
public class Window {
public static final byte
UNPRESSED = 0,
RELEASED = 1,
PRESSED = 2,
HELD = 3;
private long window;
private GLFWVidMode vidMode;
private int xOff, yOff, vWidth, vHeight;
private double scrollX, scrollY;
private StringBuilder input;
private boolean resized;
byte[] keys = new byte[GLFW_KEY_LAST];
public Window(int width, int height, boolean vSync, boolean resizable, String title) {
init(width, height, vSync, resizable, title);
}
public Window(boolean vSync) {
init(0, 0, vSync, false, null);
}
private void init(int width, int height, boolean vSync, boolean resizable, String title) {
glfwSetErrorCallback(GLFWErrorCallback.createPrint(System.err));
if(!glfwInit()) {
System.err.println("GLFW failed to initialize!");
System.exit(-1);
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);
vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
if(width == 0 || height == 0 || width >= vidMode.width() || height >= vidMode.height()) {
width = vidMode.width();
height = vidMode.height();
window = glfwCreateWindow(width, height, "", glfwGetPrimaryMonitor(), NULL);
}
else {
window = glfwCreateWindow(width, height, title, NULL, NULL);
glfwSetWindowPos(window, (vidMode.width() - width) / 2, (vidMode.height() - height) / 2);
}
glfwMakeContextCurrent(window);
createCapabilities();
glfwSwapInterval(vSync ? 1 : 0);
glfwSetWindowSizeCallback(window, new GLFWWindowSizeCallback() {
public void invoke(long window, int width, int height) {
resized = true;
glViewport(0, 0, width, height);
}
});
glfwSetScrollCallback(window, new GLFWScrollCallback() {
public void invoke(long window, double x, double y) {
scrollX = x;
scrollY = y;
}
});
input = new StringBuilder();
glfwSetCharModsCallback(window, new GLFWCharModsCallback() {
public void invoke(long window, int key, int mods) {
input.append((char) key);
}
});
glfwSetKeyCallback(window, new GLFWKeyCallback() {
public void invoke(long window, int key, int scancode, int action, int mods) {
if(action != GLFW_RELEASE) {
if(glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) != GLFW_PRESS && glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) != GLFW_PRESS && glfwGetKey(window, GLFW_KEY_LEFT_ALT) != GLFW_PRESS && glfwGetKey(window, GLFW_KEY_RIGHT_ALT) != GLFW_PRESS) {
switch (key) {
case GLFW_KEY_ENTER:
input.append((char) 10);
break;
case GLFW_KEY_BACKSPACE:
input.append((char) 8);
break;
case GLFW_KEY_DELETE:
input.append((char) 127);
break;
case GLFW_KEY_LEFT:
input.append((char) 1);
break;
case GLFW_KEY_RIGHT:
input.append((char) 2);
break;
case GLFW_KEY_UP:
input.append((char) 3);
break;
case GLFW_KEY_DOWN:
input.append((char) 4);
break;
case GLFW_KEY_TAB:
input.append((char) 9);
break;
}
}
if(keys[key] < PRESSED)
keys[key] = PRESSED;
else
keys[key] = HELD;
}
else {
if(keys[key] > RELEASED)
keys[key] = RELEASED;
else
keys[key] = UNPRESSED;
}
}
});
float vWidth = 0, vHeight = 0;
while(vWidth < vidMode.width() && vHeight < vidMode.height()) {
vWidth += 1;
vHeight += 9 /16f;
}
xOff = (int) ((vidMode.width() - vWidth) / 2);
yOff = (int) ((vidMode.height() - vHeight) / 2);
this.vWidth = (int) vWidth;
this.vHeight = (int) vHeight;
bind();
glClearColor(0, 0, 0, 1);
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
public void update() {
for (int i = 0; i < keys.length; i++) {
if(keys[i] == PRESSED)
++keys[i];
else if(keys[i] == RELEASED)
--keys[i];
}
scrollX = 0;
scrollY = 0;
resized = false;
input.delete(0, input.length());
glfwPollEvents();
}
public void clear() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
public void swap() {
glfwSwapBuffers(window);
}
public void setVSync(boolean vSync) {
glfwSwapInterval(vSync ? 1 : 0);
}
public void close() {
glfwSetWindowShouldClose(window, true);
}
public boolean shouldClose() {
return glfwWindowShouldClose(window);
}
public String getInput() {
return input.toString();
}
public byte getKey(int keyCode) {
return keys[keyCode];
}
public Vector3f getMouseCoords(Camera camera) {
double[] x = new double[1], y = new double[1];
glfwGetCursorPos(window, x, y);
Vector3f ret = new Vector3f((float) x[0] - xOff, (float) y[0] - yOff, 0);
ret.mul(camera.getWidth() / vWidth, camera.getHeight() / vHeight, 1);
return ret;
}
public boolean mousePressed(int button) {
return glfwGetMouseButton(window, button) == GLFW_PRESS;
}
public int getWidth() {
return vWidth;
}
public int getHeight() {
return vHeight;
}
public boolean wasResized() {
return resized;
}
public float getScrollX() {
return (float) scrollX;
}
public float getScrollY() {
return (float) scrollY;
}
public void bind() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(xOff, yOff, vWidth, vHeight);
}
}

View file

@ -0,0 +1,68 @@
package com.gnarly.engine.model;
import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
import static org.lwjgl.opengl.GL11.glDrawElements;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL20.glDisableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glDeleteVertexArrays;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import java.util.ArrayList;
public class Vao {
private int numAttribs = 0;
private int vao, ibo, count;
private ArrayList<Integer> vbos = new ArrayList<>();
public Vao(float[] vertices, int[] indices) {
vao = glGenVertexArrays();
glBindVertexArray(vao);
addAttribute(vertices, 3);
ibo = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
count = indices.length;
}
public void addAttribute(float[] data, int size) {
int vbo = glGenBuffers();
vbos.add(vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW);
glVertexAttribPointer(numAttribs, size, GL_FLOAT, false, 0, 0);
++numAttribs;
}
public void render() {
glBindVertexArray(vao);
for (int i = 0; i < numAttribs; ++i)
glEnableVertexAttribArray(i);
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, 0);
for (int i = 0; i < numAttribs; ++i)
glDisableVertexAttribArray(i);
}
public void destroy() {
for(int vbo : vbos)
glDeleteBuffers(vbo);
glDeleteBuffers(ibo);
glDeleteVertexArrays(vao);
}
}

View file

@ -0,0 +1,42 @@
package com.gnarly.engine.rects;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.shaders.Shader;
import com.gnarly.engine.shaders.Shader2c;
public class ColRect extends Rect {
private Shader2c shader;
private float r, g, b, a;
public ColRect(Camera camera, float x, float y, float depth, float width, float height, float r, float g, float b, float a) {
this.camera = camera;
if(vao == null)
initVao();
position = new Vector3f(x, y, depth);
rotation = 0;
sx = width / dims.x;
sy = height / dims.y;
flipX = 1;
flipY = 1;
shader = Shader.SHADER2C;
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public void render() {
shader.enable();
shader.setProjection(camera.getProjection());
shader.setView(camera.getView());
shader.setModel(new Matrix4f().translate(position).translate(dims.mul(0.5f * sx, 0.5f * sy, 1, new Vector3f())).rotateZ(rotation).scale(sx * flipX, sy * flipY, 1).translate(dims.mul(0.5f, new Vector3f()).negate()));
shader.setColor(r, g, b, a);
vao.render();
shader.disable();
}
}

View file

@ -0,0 +1,84 @@
package com.gnarly.engine.rects;
import org.joml.Vector3f;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.model.Vao;
public abstract class Rect {
protected static final Vector3f dims = new Vector3f(10, 10, 0);
protected static Vao vao = null;
protected Camera camera;
protected Vector3f position;
protected float rotation;
protected float sx, sy;
protected byte flipX, flipY;
protected void initVao() {
float[] vertices = new float[] {
0, 0, 0.0f,
0, dims.y, 0.0f,
dims.x, dims.y, 0.0f,
dims.x, 0, 0.0f
};
int[] indices = new int[] {
0, 1, 3,
3, 1, 2
};
float[] texCoords = new float[] {
0, 0,
0, 1,
1, 1,
1, 0
};
vao = new Vao(vertices, indices);
vao.addAttribute(texCoords, 2);
}
public abstract void render();
public float getX() {
return position.x;
}
public float getY() {
return position.y;
}
public float getWidth() {
return sx * dims.x;
}
public float getHeight() {
return sy * dims.y;
}
public void setRotation(float angle) {
this.rotation = (angle * 3.1415926535f) / 180f;
}
public void setPos(float x, float y) {
this.position.x = x;
this.position.y = y;
}
public void setSize(float width, float height) {
this.sx = width / dims.x;
this.sy = height / dims.y;
}
public void setFlip(int x, int y) {
this.flipX = (byte) x;
this.flipY = (byte) y;
}
public void reset(float x, float y, float width, float height) {
this.position.x = x;
this.position.y = y;
this.sx = width / dims.x;
this.sy = height / dims.y;
}
}

View file

@ -0,0 +1,94 @@
package com.gnarly.engine.rects;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.shaders.Shader;
import com.gnarly.engine.shaders.Shader2a;
import com.gnarly.engine.texture.Anim;
import com.gnarly.engine.texture.Texture;
public class TexRect extends Rect {
private Camera camera;
private Texture texture;
private Shader shader;
public TexRect(Camera camera, String texPath, float x, float y, float depth, float width, float height) {
this.camera = camera;
if(vao == null)
initVao();
position = new Vector3f(x, y, depth);
rotation = 0;
sx = width / dims.x;
sy = height / dims.y;
flipX = 1;
flipY = 1;
texture = new Texture(texPath);
shader = Shader.SHADER2T;
}
public TexRect(Camera camera, String texPath, int frames, int fps, float x, float y, float depth, float width, float height) {
this.camera = camera;
if(vao == null)
initVao();
position = new Vector3f(x, y, depth);
rotation = 0;
sx = width / dims.x;
sy = height / dims.y;
flipX = 1;
flipY = 1;
texture = new Anim(texPath, frames, fps);
shader = Shader.SHADER2A;
}
public TexRect(Camera camera, Texture texture, float x, float y, float depth, float width, float height) {
this.camera = camera;
if(vao == null)
initVao();
position = new Vector3f(x, y, depth);
rotation = 0;
sx = width / dims.x;
sy = height / dims.y;
flipX = 1;
flipY = 1;
this.texture = texture;
shader = Shader.SHADER2T;
}
public TexRect(Camera camera, Anim anim, float x, float y, float depth, float width, float height) {
this.camera = camera;
if(vao == null)
initVao();
position = new Vector3f(x, y, depth);
rotation = 0;
sx = width / dims.x;
sy = height / dims.y;
flipX = 1;
flipY = 1;
texture = anim;
shader = Shader.SHADER2A;
}
public void render() {
texture.bind();
shader.enable();
shader.setProjection(camera.getProjection());
shader.setView(camera.getView());
shader.setModel(new Matrix4f().translate(position).translate(dims.mul(0.5f * sx, 0.5f * sy, 1, new Vector3f())).rotateZ(rotation).scale(sx * flipX, sy * flipY, 1).translate(dims.mul(0.5f, new Vector3f()).negate()));
if(shader instanceof Shader2a)
((Shader2a) shader).setAnim((Anim) texture);
vao.render();
shader.disable();
texture.unbind();
}
public void pause() {
((Anim) texture).pause();
}
public void setFrame(int frame) {
((Anim) texture).setFrame(frame);
}
}

View file

@ -0,0 +1,131 @@
package com.gnarly.engine.shaders;
import static org.lwjgl.opengl.GL20.GL_COMPILE_STATUS;
import static org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER;
import static org.lwjgl.opengl.GL20.GL_VERTEX_SHADER;
import static org.lwjgl.opengl.GL20.glAttachShader;
import static org.lwjgl.opengl.GL20.glCompileShader;
import static org.lwjgl.opengl.GL20.glCreateProgram;
import static org.lwjgl.opengl.GL20.glCreateShader;
import static org.lwjgl.opengl.GL20.glDeleteShader;
import static org.lwjgl.opengl.GL20.glDetachShader;
import static org.lwjgl.opengl.GL20.glGetShaderInfoLog;
import static org.lwjgl.opengl.GL20.glGetShaderi;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import static org.lwjgl.opengl.GL20.glShaderSource;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL20.glUseProgram;
import static org.lwjgl.opengl.GL20.glValidateProgram;
import static org.lwjgl.opengl.GL32.GL_GEOMETRY_SHADER;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import org.joml.Matrix4f;
public abstract class Shader {
public static final Shader2a SHADER2A = new Shader2a();
public static final Shader2c SHADER2C = new Shader2c();
public static final Shader2t SHADER2T = new Shader2t();
public static final Shader2x SHADER2X = new Shader2x();
protected int program;
protected int projLoc, viewLoc, modelLoc;
protected Shader(String vertPath, String fragPath) {
program = glCreateProgram();
int vert = load(vertPath, GL_VERTEX_SHADER);
int frag = load(fragPath, GL_FRAGMENT_SHADER);
glAttachShader(program, vert);
glAttachShader(program, frag);
glLinkProgram(program);
glValidateProgram(program);
glDetachShader(program, vert);
glDetachShader(program, frag);
glDeleteShader(vert);
glDeleteShader(frag);
getUniformLocs();
}
protected Shader(String vertPath, String fragPath, String geomPath) {
program = glCreateProgram();
int vert = load(vertPath, GL_VERTEX_SHADER);
int frag = load(fragPath, GL_FRAGMENT_SHADER);
int geom = load(geomPath, GL_GEOMETRY_SHADER);
glAttachShader(program, vert);
glAttachShader(program, frag);
glAttachShader(program, geom);
glLinkProgram(program);
glValidateProgram(program);
glDetachShader(program, vert);
glDetachShader(program, frag);
glDetachShader(program, geom);
glDeleteShader(vert);
glDeleteShader(frag);
glDeleteShader(geom);
getUniformLocs();
}
private int load(String path, int type) {
StringBuilder file = new StringBuilder();
try {
BufferedReader reader = new BufferedReader(new FileReader(new File(path)));
String line;
while((line = reader.readLine()) != null)
file.append(line + '\n');
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
String source = file.toString();
int shader = glCreateShader(type);
glShaderSource(shader, source);
glCompileShader(shader);
if(glGetShaderi(shader, GL_COMPILE_STATUS) != 1)
throw new RuntimeException("Failed to compile shader: " + path + "! " + glGetShaderInfoLog(shader));
return shader;
}
protected void getUniformLocs() {
projLoc = glGetUniformLocation(program, "projection");
viewLoc = glGetUniformLocation(program, "view");
modelLoc = glGetUniformLocation(program, "model");
}
public void setProjection(Matrix4f projection) {
glUniformMatrix4fv(projLoc, false, projection.get(new float[16]));
}
public void setView(Matrix4f view) {
glUniformMatrix4fv(viewLoc, false, view.get(new float[16]));
}
public void setModel(Matrix4f model) {
glUniformMatrix4fv(modelLoc, false, model.get(new float[16]));
}
public void enable() {
glUseProgram(program);
}
public void disable() {
glUseProgram(0);
}
}

View file

@ -0,0 +1,24 @@
package com.gnarly.engine.shaders;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glUniform2f;
import com.gnarly.engine.texture.Anim;
public class Shader2a extends Shader {
private int animLoc;
protected Shader2a() {
super("res/shaders/s2a/vert.vs", "res/shaders/s2a/frag.fs");
}
protected void getUniformLocs() {
super.getUniformLocs();
animLoc = glGetUniformLocation(program, "animProps");
}
public void setAnim(Anim anim) {
glUniform2f(animLoc, anim.getFrameWidth(), anim.getOffset());
}
}

View file

@ -0,0 +1,22 @@
package com.gnarly.engine.shaders;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glUniform4f;
public class Shader2c extends Shader {
int colorLoc;
protected Shader2c() {
super("res/shaders/s2c/vert.vs", "res/shaders/s2c/frag.fs");
}
protected void getUniformLocs() {
super.getUniformLocs();
colorLoc = glGetUniformLocation(program, "iColor");
}
public void setColor(float r, float g, float b, float a) {
glUniform4f(colorLoc, r, g, b, a);
}
}

View file

@ -0,0 +1,8 @@
package com.gnarly.engine.shaders;
public class Shader2t extends Shader {
protected Shader2t() {
super("res/shaders/s2t/vert.vs", "res/shaders/s2t/frag.fs");
}
}

View file

@ -0,0 +1,30 @@
package com.gnarly.engine.shaders;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glUniform2f;
import static org.lwjgl.opengl.GL20.glUniform4f;
public class Shader2x extends Shader {
private int charLoc;
private int colorLoc;
protected Shader2x() {
super("res/shaders/s2x/vert.vs", "res/shaders/s2x/frag.fs");
}
protected void getUniformLocs() {
super.getUniformLocs();
colorLoc = glGetUniformLocation(program, "iColor");
charLoc = glGetUniformLocation(program, "character");
}
public void setColor(float r, float g, float b, float a) {
glUniform4f(colorLoc, r, g, b, a);
}
public void setChar(int character) {
int column = character % 16, row = character / 16;
glUniform2f(charLoc, (float) column / 16f, (float) row / 16f);
}
}

View file

@ -0,0 +1,82 @@
package com.gnarly.engine.text;
import org.joml.Matrix4f;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.model.Vao;
import com.gnarly.engine.shaders.Shader;
import com.gnarly.engine.shaders.Shader2x;
import com.gnarly.engine.texture.Texture;
public class Font {
private final int
WIDTH = 5,
HEIGHT = 8;
private Camera camera;
private Texture texture;
private Shader2x shader;
private Vao vao;
public Font(Camera camera, String fontPath) {
this.camera = camera;
texture = new Texture(fontPath);
shader = Shader.SHADER2X;
float[] vertices = new float[] {
0, 0, 0,
0, HEIGHT, 0,
WIDTH, HEIGHT, 0,
WIDTH, 0, 0
};
int[] indices = new int[] {
0, 1, 3,
3, 1, 2
};
float[] texCoords = new float[] {
0, 0,
0, 0.0625f,
0.0625f, 0.0625f,
0.0625f, 0
};
vao = new Vao(vertices, indices);
vao.addAttribute(texCoords, 2);
}
public void drawString(String string, float x, float y, float depth, float height, float r, float g, float b, float a) {
float scale = height / (float) HEIGHT;
texture.bind();
shader.enable();
shader.setProjection(camera.getProjection());
shader.setView(camera.getView());
shader.setColor(r, g, b, a);
char[] chars = string.toCharArray();
float lx = x;
for (char c : chars) {
if(c == '\n') {
y += (float) (HEIGHT + 1) * scale;
lx = x;
}
else {
if(c != ' ') {
shader.setModel(new Matrix4f().translate(lx, y, depth).scale(scale, scale, 0));
shader.setChar(c);
vao.render();
}
lx += (float) (WIDTH + 1) * scale;
}
}
}
public float getWidth(float height) {
return (height / (float) HEIGHT) * (WIDTH + 1);
}
public float getCharWidth(float height) {
return (height / (float) HEIGHT) * WIDTH;
}
public float getHeight(float height) {
return (height / (float) HEIGHT) * (HEIGHT + 1);
}
}

View file

@ -0,0 +1,57 @@
package com.gnarly.engine.texture;
public class Anim extends Texture {
private final float FRAME_WIDTH;
private final long NANO_PER_FRAME;
private final int NUM_FRAMES;
private int curFrame;
private long startTime;
private boolean playing;
public Anim(String path, int numFrames, int fps) {
super(path);
FRAME_WIDTH = 1f / (float) numFrames;
NANO_PER_FRAME = 1000000000l / fps;
this.curFrame = 0;
this.NUM_FRAMES = numFrames;
startTime = System.nanoTime();
playing = true;
}
@Override
public void bind() {
super.bind();
if(playing) {
int frame = (int) ((System.nanoTime() - startTime) / NANO_PER_FRAME);
curFrame = frame % NUM_FRAMES;
}
}
public void play() {
startTime = System.nanoTime();
playing = true;
}
public void pause() {
playing = false;
}
@Override
public int getWidth() {
return width / NUM_FRAMES;
}
public float getFrameWidth() {
return FRAME_WIDTH;
}
public float getOffset() {
return FRAME_WIDTH * curFrame;
}
public void setFrame(int frame) {
curFrame = frame;
}
}

View file

@ -0,0 +1,83 @@
package com.gnarly.engine.texture;
import static org.lwjgl.opengl.GL11.GL_NEAREST;
import static org.lwjgl.opengl.GL11.GL_RGBA;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
import static org.lwjgl.opengl.GL11.glBindTexture;
import static org.lwjgl.opengl.GL11.glGenTextures;
import static org.lwjgl.opengl.GL11.glTexImage2D;
import static org.lwjgl.opengl.GL11.glTexParameterf;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import org.lwjgl.BufferUtils;
public class Texture {
protected int id, width, height;
public Texture(String fileName) {
try {
BufferedImage bi = ImageIO.read(new File(fileName));
width = bi.getWidth();
height = bi.getHeight();
int[] pixelsRaw = new int[width * height];
pixelsRaw = bi.getRGB(0, 0, width, height, null, 0, width);
ByteBuffer pixels = BufferUtils.createByteBuffer(width * height * 4);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int pixel = pixelsRaw[i * width + j];
pixels.put((byte)((pixel >> 16) & 0xFF)); //RED
pixels.put((byte)((pixel >> 8) & 0xFF)); //GREEN
pixels.put((byte)((pixel ) & 0xFF)); //BLUE
pixels.put((byte)((pixel >> 24) & 0xFF)); //ALPHA
}
}
pixels.flip();
id = glGenTextures();
bind();
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
unbind();
} catch(IOException e) {
e.printStackTrace();
}
}
public Texture(int id) {
this.id = id;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public void bind() {
glBindTexture(GL_TEXTURE_2D, id);
}
public void unbind() {
glBindTexture(GL_TEXTURE_2D, 0);
}
}

View file

@ -0,0 +1,116 @@
package com.gnarly.game;
import java.io.File;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.display.Window;
import com.gnarly.engine.rects.ColRect;
import com.gnarly.engine.rects.TexRect;
import com.gnarly.engine.text.Font;
import com.gnarly.game.board.Map;
import com.gnarly.game.objs.Button;
import com.gnarly.game.text.Console;
import com.gnarly.game.text.EditorManager;
import com.gnarly.game.text.TextBox;
public class GamePanel {
private Window window;
private Camera camera;
private Map map;
private EditorManager editor;
private Button run, stop, menu;
private TexRect header;
private TextBox background;
private ColRect darkener;
private Button next, smenu;
private Font font;
private boolean success;
private int level = 1;
private int max;
private int state = 1;
public GamePanel(Window window, Camera camera) {
max = new File("res/maps").list().length;
this.window = window;
this.camera = camera;
map = new Map(camera, "res/maps/level1.llm");
editor = new EditorManager(window, camera);
run = new Button(window, camera, "res/img/map/buttons/run/unpressed.png", "res/img/map/buttons/run/hovered.png", "res/img/map/buttons/run/pressed.png", 0, 324, 0, 75, 24);
stop = new Button(window, camera, "res/img/map/buttons/stop/unpressed.png", "res/img/map/buttons/stop/hovered.png", "res/img/map/buttons/stop/pressed.png", 76, 324, 0, 76, 24);
menu = new Button(window, camera, "res/img/map/buttons/menu/unpressed.png", "res/img/map/buttons/menu/hovered.png", "res/img/map/buttons/menu/pressed.png", 153, 324, 0, 75, 24);
header = new TexRect(camera, "res/img/header.png", 0, 0, 0, 228, 32);
background = new TextBox(camera, "", 220, 100, -0.5f, 200, 100, 24);
darkener = new ColRect(camera, 0, 0, -0.25f, 640, 360, 0, 0, 0, 0.5f);
next = new Button(window, camera, "res/img/menu/next/unpressed.png", "res/img/menu/next/hovered.png", "res/img/menu/next/pressed.png", 362, 182, -0.75f, 16, 16);
smenu = new Button(window, camera, "res/img/map/buttons/smenu/unpressed.png", "res/img/map/buttons/smenu/hovered.png", "res/img/map/buttons/smenu/pressed.png", 262, 182, -0.75f, 16, 16);
font = new Font(camera, "res/img/fonts/default.png");
}
public void update() {
if(!success) {
editor.update();
map.update();
run.update();
stop.update();
menu.update();
if(run.getState() == Button.RELEASED) {
Console.error.clear();
map.execute(editor.getCommands());
}
else if(stop.getState() == Button.RELEASED)
map.fullStop();
else if(menu.getState() == Button.RELEASED)
state = 0;
success = map.success();
if(success) {
++level;
if(level > max)
level = 1;
}
}
else {
next.update();
smenu.update();
if(next.getState() == Button.RELEASED) {
map = new Map(camera, "res/maps/level" + level + ".llm");
editor = new EditorManager(window, camera);
success = false;
}
else if(smenu.getState() == Button.RELEASED) {
map = new Map(camera, "res/maps/level" + level + ".llm");
editor = new EditorManager(window, camera);
success = false;
state = 0;
}
}
}
public void render() {
header.render();
run.render();
stop.render();
menu.render();
map.render();
editor.render();
if(success) {
font.drawString("SUCCESS!", (640 - font.getWidth(24) * 7 - font.getCharWidth(24)) / 2, 106, -0.75f, 24, 1, 1, 1, 1);
darkener.render();
background.render();
next.render();
smenu.render();
}
}
public int getState() {
int temp = state;
state = 1;
return temp;
}
}

View file

@ -0,0 +1,90 @@
package com.gnarly.game;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.display.Window;
public class Main implements Runnable {
public static final int BIG_TICK = 8;
private final int TICK_RATE = 64;
private final int FPS = 59;
public static int tick = 0;
public static int fps = 0;
private int state;
private Window window;
private Camera camera;
private MenuPanel menu;
private GamePanel panel;
public void start() {
Thread gameLoop = new Thread(this, "OpenGL");
gameLoop.start();
}
public void run() {
long pastTime, curTime, npf = 1000000000 / FPS, pastSec, pastUTime, npt = 1000000000 / TICK_RATE;
int frames = 0;
init();
pastTime = System.nanoTime();
pastUTime = pastTime;
pastSec = pastTime;
while(!window.shouldClose()) {
curTime = System.nanoTime();
while(curTime - pastUTime >= npt) {
update();
pastUTime += npt;
++tick;
}
curTime = System.nanoTime();
if(curTime - pastTime > npf) {
render();
pastTime += npf;
++frames;
}
if(curTime - pastSec >= 1000000000) {
fps = frames;
frames = 0;
pastSec += 1000000000;
}
}
}
private void init() {
state = 0;
window = new Window(false);
camera = new Camera(640, 360);
menu = new MenuPanel(window, camera);
panel = new GamePanel(window, camera);
window.bind();
}
private void update() {
window.update();
if(state == 0) {
menu.update();
state = menu.getState();
}
else {
panel.update();
state = panel.getState();
}
}
private void render() {
window.clear();
if(state == 0)
menu.render();
else
panel.render();
window.bind();
window.swap();
}
public static void main(String[] args) {
new Main().start();
}
}

View file

@ -0,0 +1,104 @@
package com.gnarly.game;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.display.Window;
import com.gnarly.engine.rects.TexRect;
import com.gnarly.engine.text.Font;
import com.gnarly.game.objs.Button;
public class MenuPanel {
private Window window;
private Camera camera;
private Font font;
private Button play, help, close, next, prev;
private TexRect background;
private TexRect helpMenu;
private int state = 0, page = 0;
private boolean showHelp = false;
private String[] headers, content;
public MenuPanel(Window window, Camera camera) {
this.window = window;
this.camera = camera;
font = new Font(camera, "res/img/fonts/default.png");
background = new TexRect(camera, "res/img/menu/background.png", 0, 0, 0, 640, 360);
play = new Button(window, camera, "res/img/menu/play/unpressed.png", "res/img/menu/play/hovered.png", "res/img/menu/play/pressed.png", 260, 160, 0, 120, 32);
help = new Button(window, camera, "res/img/menu/help/unpressed.png", "res/img/menu/help/hovered.png", "res/img/menu/help/pressed.png", 260, 200, 0, 120, 32);
close = new Button(window, camera, "res/img/menu/close/unpressed.png", "res/img/menu/close/hovered.png", "res/img/menu/close/pressed.png", 384, 110, 0, 16, 16);
next = new Button(window, camera, "res/img/menu/next/unpressed.png", "res/img/menu/next/hovered.png", "res/img/menu/next/pressed.png", 384, 264, 0, 16, 16);
prev = new Button(window, camera, "res/img/menu/prev/unpressed.png", "res/img/menu/prev/hovered.png", "res/img/menu/prev/pressed.png", 240, 264, 0, 16, 16);
helpMenu = new TexRect(camera, "res/img/menu/help/helpMenu.png", 240, 110, 0, 160, 170);
headers = new String[8];
headers[0] = "OVERVIEW";
headers[1] = "GOAL";
headers[2] = "PROGRAMMING";
headers[3] = "FIRE";
headers[4] = "ROTATE";
headers[5] = "WAIT";
headers[6] = "THREADS";
headers[7] = "TIMING";
content = new String[8];
content[0] = "SO WHAT THE HECK IS THIS GAME?\n\nWELL BASICALLY ITS A PROGRAMMING\nGAME IN WHICH THE PLAYER CONTROLS\nMIRRORS IN ORDER TO DIRECT A LASER TO\nA TRIGGER.";
content[1] = "THE GOAL OF THE GAME IS TO BEAT\nALL THE LEVELS. THE EXACT GOAL\nOF EACH LEVEL VARIES BUT THE GOAL\nIS ALWAYS ABOUT TRIGGERING A TRIGGER\nWITH A LASER. WHICH TRIGGER AND AT\nWHAT TIMES ARE LEVEL SPECIFIC.\nTHE BOX AT THE TOP WILL TELL\nYOU THE GOAL FOR THAT LEVEL";
content[2] = "TO CONTROL THE GAME BOARD YOU USE THE\nPROGRAMMING INTRFACE ON THE LEFT.\n\nTHERE ARE THREE COMMANDS YOU CAN USE.\n\n- FIRE\n- ROTATE\n- WAIT";
content[3] = "THE FIRE COMMAND DOES EXACTLY WHAT\nIT SAYS. IT FIRES A LASER.\n\nUSAGE:\n\nFIRE X\n\n - X IS THE X COMPONENT OF\n THE LOCATION OF THE\n CANNON YOU WANT TO FIRE.";
content[4] = "THE ROTATE COMMAND ROTATES A MIRROR\n90 DEGREES.\n\nUSAGE:\n\nROTATE X Y\n\n - X IS THE X COMPONENT OF\n THE LOCATION OF THE\n THE MIRROR YOU WANT ROTATED\n\n - Y IS THE Y COMPONENT OF\n THE LOCATION OF THE\n THE MIRROR YOU WANT ROTATED";
content[5] = "THE WAIT COMMAND PAUSES THE CURRENT\nTHREAD FOR A CERTAIN NUMBER FO TICKS.\n\nUSAGE:\n\nWAIT X\n\n - X IS THE NUMBER OF TICKS\n YOU WANT THE THREAD TO PAUSE";
content[6] = "YOU HAVE FOUR THREADS AT YOUR DISPOAL.\nEACH THREAD CAN ONLY HOUSE A\nCERTAIN NUMBER OF COMMANDS.\nTHE MAIN ADVANTAGE OF MUTIPLE THREADS\nIS IT ALLOWS YOU TO RUN MUTIPLE\nCOMMANDS SIMULTANEOUSLY.";
content[7] = "THE WHOLE GAME RUNS ON A SYSTEM\nOF TICKS. EACH TICK IS 1/16TH\nOF A SECOND. DURING ONE TICK\nONE COMMAND FROM EACH THREAD\nIS EXECUTED.";
}
public void update() {
if(!showHelp) {
play.update();
help.update();
if(play.getState() == Button.RELEASED)
state = 1;
else if(help.getState() == Button.RELEASED)
showHelp = true;
}
else {
close.update();
next.update();
prev.update();
if(close.getState() == Button.RELEASED) {
page = 0;
showHelp = false;
}
else if(next.getState() == Button.RELEASED && page < headers.length - 1)
++page;
else if(prev.getState() == Button.RELEASED && page > 0)
--page;
}
}
public void render() {
if(!showHelp) {
play.render();
help.render();
}
else {
close.render();
next.render();
prev.render();
String pageString = "[" + (page + 1) + "/" + headers.length + "]";
font.drawString(pageString, (640 - (pageString.length() * 5 + pageString.length() - 1)) / 2, 269, 0, 8, 1, 1, 1, 1);
font.drawString(headers[page], 243, 113, 0, 16, 1, 1, 1, 1);
font.drawString(content[page], 243, 136, 0, 5.3333333333f, 1, 1, 1, 1);
helpMenu.render();
}
background.render();
}
public int getState() {
int temp = state;
state = 0;
return temp;
}
}

View file

@ -0,0 +1,308 @@
package com.gnarly.game.board;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
import org.joml.Vector3f;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.rects.TexRect;
import com.gnarly.game.Main;
import com.gnarly.game.board.laser.Cannon;
import com.gnarly.game.board.laser.Laser;
import com.gnarly.game.board.laser.Trigger;
import com.gnarly.game.commands.Command;
import com.gnarly.game.commands.Fire;
import com.gnarly.game.commands.Rotate;
import com.gnarly.game.commands.Wait;
import com.gnarly.game.text.Console;
import com.gnarly.game.text.TextBox;
public class Map {
public static final int
EMPTY = 0,
M45 = 1,
M135 = 2,
CANNON = 3,
TRIGGER = 4;
private static final int
OFFSET = 14,
WIDTH = 32,
HEIGHT = 32,
COLUMNS = 12,
ROWS = 10;
private String mapPath;
private Console console;
private Camera camera;
private TexRect[] states;
private int map[][];
private TexRect topbar, leftbar;
private TexRect[] corners, nums;
private float x, y;
private TextBox ghead, goals;
private ArrayList<Laser> lasers;
private ArrayList<Cannon> cannons;
private ArrayList<Trigger> triggers;
private boolean execute, lastRan;
private ArrayList<Command>[] commands;
private boolean success = false;
public Map(Camera camera, String mapPath) {
this.mapPath = mapPath;
this.camera = camera;
this.x = camera.getWidth() - getWidth();
this.y = 0;
ghead = new TextBox(camera, "GOALS\nTRIG |TICK", 182, 33, 0, 46, 15, 5.3333333f);
goals = new TextBox(camera, "", 182, 49, 0, 46, 185, 5.3333333f);
map = new int[COLUMNS][ROWS];
states = new TexRect[5];
states[EMPTY] = new TexRect(camera, "res/img/map/empty.png", 0, 0, 0, 32, 32);
states[M45] = new TexRect(camera, "res/img/map/mirror45.png", 0, 0, 0, 32, 32);
states[M135] = new TexRect(camera, "res/img/map/mirror135.png", 0, 0, 0, 32, 32);
states[CANNON] = new TexRect(camera, "res/img/map/cannon.png", 10, 8, 0, 0, 0, 32, 32);
states[TRIGGER] = new TexRect(camera, "res/img/map/trigger.png", 32, 32, 0, 0, 0, 32, 32);
states[TRIGGER].pause();
corners = new TexRect[4];
corners[0] = new TexRect(camera, "res/img/bars/tlcorner.png", x, y, 0, OFFSET, OFFSET);
corners[1] = new TexRect(camera, "res/img/bars/blcorner.png", x, y + getHeight() - OFFSET, 0, OFFSET, OFFSET);
corners[2] = new TexRect(camera, "res/img/bars/brcorner.png", x + getWidth()- OFFSET, y + getHeight() - OFFSET, 0, OFFSET, OFFSET);
corners[3] = new TexRect(camera, "res/img/bars/trcorner.png", x + getWidth()- OFFSET, y, 0, OFFSET, OFFSET);
topbar = new TexRect(camera, "res/img/bars/topbar.png", 0, 0, 0, WIDTH, OFFSET);
leftbar = new TexRect(camera, "res/img/bars/leftbar.png", 0, 0, 0, OFFSET, HEIGHT);
lasers = new ArrayList<>();
cannons = new ArrayList<>();
triggers = new ArrayList<>();
nums = new TexRect[12];
for (int i = 0; i < nums.length; i++)
nums[i] = new TexRect(camera, "res/img/bars/numbers/" + i + ".png", 0, 0, 0, 8, 8);
loadMap();
console = new Console(camera);
}
public void update() {
if(Main.tick % Main.BIG_TICK == 0) {
boolean ran = false;
if(execute || lasers.size() > 0) {
ran = true;
if(commands != null) {
String[] commandStrings = new String[4];
for (int i = 0; i < commandStrings.length; i++)
commandStrings[i] = new String();
for (int i = 0; i < 4; i++) {
if(commands[i].size() > 0) {
commandStrings[i] = commands[i].get(0).getCommand();
Command command = commands[i].get(0);
if(command instanceof Wait) {
Wait wait = (Wait) command;
wait.decrement();
if(wait.getTicks() == 0)
commands[i].remove(0);
}
else if(command instanceof Rotate) {
Rotate rotate = (Rotate) command;
int x = rotate.getX();
int y = rotate.getY() + 1;
if(map[x][y] == M45)
map[x][y] = M135;
else if(map[x][y] == M135)
map[x][y] = M45;
else {
Console.error.add("LOCATION " + x + " " + y + " IS NOT A MIRROR");
execute = false;
}
commands[i].remove(0);
}
else if(command instanceof Fire) {
Fire fire = (Fire) command;
fire(fire.getCannon());
commands[i].remove(0);
}
}
}
int num = 0;
for (int i = 0; i < 4; i++)
if(commands[i].size() > 0)
++num;
if(num == 0) {
commands = null;
execute = false;
}
console.printCommands(commandStrings);
}
}
if(!ran && lastRan) {
for (int i = 0; i < triggers.size(); i++) {
triggers.get(i).update();
if(!triggers.get(i).success())
Console.error.add("FAILED TO MEET ALL TRIGGER CONDITIONS!");
}
if(Console.error.size() == 0)
success = true;
for (int i = 0; i < triggers.size(); i++)
triggers.get(i).reset();
fullStop();
}
lastRan = ran;
}
for (int i = 0; i < lasers.size(); i++)
lasers.get(i).update();
for (int i = 0; i < lasers.size(); i++)
if(lasers.get(i).done())
lasers.remove(i);
if(execute || lasers.size() > 0)
for (int i = 0; i < triggers.size(); i++)
triggers.get(i).update();
}
public void render() {
ghead.render();
goals.render();
console.render();
for (int i = 0; i < lasers.size(); i++)
lasers.get(i).render();
for (int i = 0; i < corners.length; i++)
corners[i].render();
for (int i = 0; i < map.length; i++) {
nums[i].setPos(x + i * WIDTH + OFFSET + 12, y + 3);
nums[i].render();
topbar.setPos(x + i * WIDTH + OFFSET, y);
topbar.render();
nums[i].setPos(x + i * WIDTH + OFFSET + 12, y + getHeight() - OFFSET + 3);
nums[i].render();
topbar.setPos(x + i * WIDTH + OFFSET, y + getHeight() - OFFSET);
topbar.render();
}
for (int i = 0; i < map[0].length; i++) {
if (i > 0 && i < map[0].length - 1) {
nums[i - 1].setPos(x + 3, y + i * HEIGHT + OFFSET + 12);
nums[i - 1].render();
nums[i - 1].setPos(x + getWidth() - OFFSET + 3, y + i * HEIGHT + OFFSET + 12);
nums[i - 1].render();
}
leftbar.setPos(x, y + i * HEIGHT + OFFSET);
leftbar.render();
leftbar.setPos(x + getWidth() - OFFSET, y + i * HEIGHT + OFFSET);
leftbar.render();
}
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[0].length; j++) {
int state = map[i][j];
if(state != TRIGGER) {
states[state].setPos(x + i * WIDTH + OFFSET, y + j * HEIGHT + OFFSET);
states[state].render();
}
}
}
for (int i = 0; i < triggers.size(); i++) {
Trigger trigger = triggers.get(i);
states[TRIGGER].setFrame(trigger.getFrame());
states[TRIGGER].setPos(x + trigger.getX() * WIDTH + OFFSET, y + trigger.getY() * HEIGHT + OFFSET);
states[TRIGGER].render();
}
}
public static float getWidth() {
return WIDTH * COLUMNS + OFFSET * 2;
}
public static float getHeight() {
return HEIGHT * ROWS + OFFSET * 2;
}
private void fire(int cannonNum) {
Cannon cannon = null;
for (int i = 0; i < cannons.size() && cannon == null; i++) {
if(cannons.get(i).getX() == cannonNum)
cannon = cannons.get(i);
}
if(cannon == null)
Console.error.add("NO CANNON AT POSITION " + cannonNum + "!");
else {
int x = cannon.getX();
int y = cannon.getY();
Vector3f dir = cannon.getDir();
x += dir.x;
y += dir.y;
lasers.add(new Laser(camera, dir, this.x + OFFSET, this.y + OFFSET, x, y, map, triggers));
}
}
private void loadMap() {
Scanner scanner = null;
try {
triggers.clear();
cannons.clear();
scanner = new Scanner(new File(mapPath));
for (int j = 0; j < this.map[0].length; j++) {
for (int i = 0; i < this.map.length; i++) {
this.map[i][j] = scanner.nextInt();
if(this.map[i][j] == CANNON)
cannons.add(new Cannon(i, j, Laser.UP));
if(this.map[i][j] == TRIGGER)
triggers.add(new Trigger(i, j));
}
}
int numGoals = scanner.nextInt();
String goals = new String();
for (int i = 0; i < numGoals; i++) {
String curGoal = new String();
int trigger = scanner.nextInt();
int tickNum = scanner.nextInt();
curGoal += triggers.get(trigger).getX();
while(curGoal.length() < 4)
curGoal += ' ';
curGoal += " |";
curGoal += tickNum + "\n";
goals += curGoal;
triggers.get(trigger).addCondition(tickNum);
}
this.goals.setText(goals);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
scanner.close();
}
}
public void execute(ArrayList<Command>[] commands) {
if(Console.error.size() == 0) {
lasers.clear();
console.clear();
execute = true;
this.commands = commands;
}
}
public void fullStop() {
loadMap();
lasers.clear();
execute = false;
commands = null;
}
public void setMap(String mapPath) {
this.mapPath = mapPath;
loadMap();
}
public int[][] getMap() {
return map;
}
public boolean success() {
return success;
}
}

View file

@ -0,0 +1,27 @@
package com.gnarly.game.board.laser;
import org.joml.Vector3f;
public class Cannon {
int x, y;
Vector3f dir;
public Cannon(int x, int y, Vector3f dir) {
this.x = x;
this.y = y;
this.dir = dir;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Vector3f getDir() {
return dir;
}
}

View file

@ -0,0 +1,182 @@
package com.gnarly.game.board.laser;
import java.util.ArrayList;
import org.joml.Vector3f;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.rects.TexRect;
import com.gnarly.engine.texture.Anim;
import com.gnarly.game.Main;
import com.gnarly.game.board.Map;
public class Laser {
public static final Vector3f
UP = new Vector3f(0, -1, 0),
DOWN = new Vector3f(0, 1, 0),
LEFT = new Vector3f(-1, 0, 0),
RIGHT = new Vector3f(1, 0, 0);
private final int
STRAIGHT = 0,
BEND = 1,
TRIGGER = 2,
SOLID = 3;
private Anim[] fanims;
private TexRect[] frects;
private Anim[] banims;
private TexRect[] brects;
private float startX, startY;
private int fx, fy, ftype, frame;
private int[] props;
private Vector3f fdir;
private int bx, by, btype;
private boolean renderFront, renderBack;
private ArrayList<Trigger> triggers;
private int[][] map;
public Laser(Camera camera, Vector3f dir, float startX, float startY, int x, int y, int[][] map, ArrayList<Trigger> triggers) {
this.startX = startX;
this.startY = startY;
bx = 0;
by = 0;
fx = x;
fy = y;
fdir = dir;
this.map = map;
this.triggers = triggers;
renderFront = true;
renderBack = false;
initAnims(camera);
}
private void initAnims(Camera camera) {
banims = new Anim[2];
banims[0] = new Anim("res/img/map/laser/laserStraightBack.png", 32, 64);
banims[1] = new Anim("res/img/map/laser/laserBendBack.png", 32, 64);
brects = new TexRect[2];
brects[STRAIGHT] = new TexRect(camera, banims[0], 0, 0, 0, 32, 32);
brects[BEND] = new TexRect(camera, banims[1], 0, 0, 0, 32, 32);
for (int i = 0; i < banims.length; i++)
banims[i].pause();
fanims = new Anim[2];
fanims[0] = new Anim("res/img/map/laser/laserStraight.png", 32, 64);
fanims[1] = new Anim("res/img/map/laser/laserBend.png", 32, 64);
frects = new TexRect[2];
frects[STRAIGHT] = new TexRect(camera, fanims[0], 0, 0, 0, 32, 32);
frects[BEND] = new TexRect(camera, fanims[1], 0, 0, 0, 32, 32);
for (int i = 0; i < fanims.length; i++)
fanims[i].pause();
}
public void update() {
frame = Main.tick % Main.BIG_TICK * (32 / Main.BIG_TICK);
if(frame == 0) {
if(bx < 0 || by < 0 || bx >= map.length || by >= map[0].length || getType(bx, by) >= TRIGGER)
renderBack = false;
else if(props != null) {
btype = ftype;
brects[btype].setPos(startX + bx * 32, startY + by * 32);
brects[btype].setRotation(props[0]);
brects[btype].setFlip(1, props[1]);
renderBack = true;
}
if(fx < 0 || fy < 0 || fx >= map.length || fy >= map[0].length || getType(fx, fy) == SOLID)
renderFront = false;
else if(getType(fx, fy) == TRIGGER) {
for (int i = 0; i < triggers.size(); i++) {
if(triggers.get(i).getX() == fx && triggers.get(i).getY() == fy)
triggers.get(i).trigger();
}
renderFront = false;
}
else {
ftype = getType(fx, fy);
props = getProps(fx, fy, fdir);
frects[ftype].setPos(startX + fx * 32, startY + fy * 32);
frects[ftype].setRotation(props[0]);
frects[ftype].setFlip(1, props[1]);
fdir = getDir(fx, fy, fdir);
}
bx = fx;
by = fy;
fx += fdir.x;
fy += fdir.y;
}
}
private int getType(int x, int y) {
if(map[x][y] == 0)
return STRAIGHT;
else if(map[x][y] >= 1 && map[x][y] <= 2)
return BEND;
else if(map[x][y] == 4)
return TRIGGER;
else
return SOLID;
}
private int[] getProps(int x, int y, Vector3f dir) {
if((dir.x == 1 && dir.y == 0 && map[x][y] == Map.M45))
return new int[] {0, 1};
else if((dir.x == 1 && dir.y == 0 && map[x][y] == Map.M135))
return new int[] {0, -1};
else if((dir.x == 1 && dir.y == 0 && map[x][y] == Map.EMPTY))
return new int[] {90, 1};
else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.M45))
return new int[] {180, 1};
else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.M135))
return new int[] {180, -1};
else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.EMPTY))
return new int[] {90, -1};
else if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.M45))
return new int[] {90, -1};
else if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.M135))
return new int[] {90, 1};
else if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.EMPTY))
return new int[] {0, -1};
else if((dir.x == 0 && dir.y == -1 && map[x][y] == Map.M45))
return new int[] {270, -1};
else if((dir.x == 0 && dir.y == -1 && map[x][y] == Map.M135))
return new int[] {270, 1};
else if((dir.x == 0 && dir.y == -1 && map[x][y] == Map.EMPTY))
return new int[] {0, 1};
return null;
}
private Vector3f getDir(int x, int y, Vector3f dir) {
if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.M45) || (dir.x == 0 && dir.y == -1 && map[x][y] == Map.M135))
return LEFT;
else if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.M135) || (dir.x == 0 && dir.y == -1 && map[x][y] == Map.M45))
return RIGHT;
else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.M135) || (dir.x == 1 && dir.y == 0 && map[x][y] == Map.M45))
return UP;
else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.M45) || (dir.x == 1 && dir.y == 0 && map[x][y] == Map.M135))
return DOWN;
else if(map[x][y] == Map.EMPTY)
return dir;
return null;
}
public void render() {
if(renderBack) {
banims[btype].setFrame(frame);
brects[btype].render();
}
if(renderFront) {
fanims[ftype].setFrame(frame);
frects[ftype].render();
}
}
public boolean done() {
return !renderBack && !renderFront;
}
}

View file

@ -0,0 +1,79 @@
package com.gnarly.game.board.laser;
import java.util.ArrayList;
import com.gnarly.game.Main;
import com.gnarly.game.text.Console;
public class Trigger {
private int tick = 0;
private int frame = 0;
private int triggered = 0;
private int x, y, met;
private ArrayList<Integer> conditions;
public Trigger(int x, int y) {
this.x = x;
this.y = y;
conditions = new ArrayList<>();
}
public void update() {
if(triggered == 2) {
frame = Main.tick % Main.BIG_TICK * (32 / Main.BIG_TICK);
if(Main.tick % Main.BIG_TICK == Main.BIG_TICK - 1)
--triggered;
}
else
frame = 0;
if(Main.tick % Main.BIG_TICK == 0) {
boolean check = false;
for (int i = 0; i < conditions.size(); i++) {
if(conditions.get(i).intValue() == tick) {
check = true;
if(triggered == 0)
Console.error.add("TRIGGER FAILED TO BE TRIGGERED AT PROPER TIME!");
else
++met; }
}
if(!check && triggered == 2) {
--met;
Console.error.add("TRIGGER WAS TRIGGERED AT AN UNEXPECTED TIME!");
}
++tick;
}
}
public void trigger() {
triggered = 2;
}
public int getFrame() {
return frame;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void addCondition(int condition) {
conditions.add(condition);
}
public void reset() {
conditions.clear();
tick = 0;
met = 0;
}
public boolean success() {
return met == conditions.size();
}
}

View file

@ -0,0 +1,5 @@
package com.gnarly.game.commands;
public interface Command {
public String getCommand();
}

View file

@ -0,0 +1,18 @@
package com.gnarly.game.commands;
public class Fire implements Command {
private int cannon;
public Fire(int cannon) {
this.cannon = cannon;
}
public int getCannon() {
return cannon;
}
public String getCommand() {
return "FIRE " + cannon;
}
}

View file

@ -0,0 +1,23 @@
package com.gnarly.game.commands;
public class Rotate implements Command {
private int x, y;
public Rotate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public String getCommand() {
return "ROTATE " + x + " " + y;
}
}

View file

@ -0,0 +1,22 @@
package com.gnarly.game.commands;
public class Wait implements Command {
private int ticks;
public Wait(int ticks) {
this.ticks = ticks;
}
public int getTicks() {
return ticks;
}
public void decrement() {
--ticks;
}
public String getCommand() {
return "WAIT " + ticks;
}
}

View file

@ -0,0 +1,77 @@
package com.gnarly.game.objs;
import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_1;
import org.joml.Vector3f;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.display.Window;
import com.gnarly.engine.rects.TexRect;
public class Button {
public static final int
UNPRESSED = 0,
RELEASED = 1,
PRESSED = 2,
HELD = 3;
private Window window;
private Camera camera;
private TexRect[] states;
private float x, y, width, height;
private int state, tex;
public Button(Window window, Camera camera, String tex1, String tex2, String tex3, float x, float y, float depth, float width, float height) {
this.window = window;
this.camera = camera;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
states = new TexRect[3];
states[0] = new TexRect(camera, tex1, x, y, depth, width, height);
states[1] = new TexRect(camera, tex2, x, y, depth, width, height);
states[2] = new TexRect(camera, tex3, x, y, depth, width, height);
tex = 0;
state = 0;
}
public void update() {
if(contains(window.getMouseCoords(camera))) {
if(window.mousePressed(GLFW_MOUSE_BUTTON_1)) {
tex = 2;
if(state <= RELEASED)
state = PRESSED;
else
state = HELD;
}
else {
tex = 1;
if(state >= PRESSED)
state = RELEASED;
else
state = UNPRESSED;
}
}
else {
tex = 0;
state = UNPRESSED;
}
}
public void render() {
states[tex].render();
}
public boolean contains(Vector3f coords) {
return coords.x >= x && coords.y >= y && coords.x < x + width && coords.y < y + height;
}
public int getState() {
return state;
}
}

View file

@ -0,0 +1,79 @@
package com.gnarly.game.text;
import java.util.ArrayList;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.rects.ColRect;
import com.gnarly.engine.text.Font;
public class Console {
public static ArrayList<String> error = new ArrayList<>();
private Camera camera;
private ColRect background;
private ColRect body;
private Font font;
private float x, y, width, height;
ArrayList<String> console;
private int maxLength;
private int maxHeight;
private int viewY;
public Console(Camera camera) {
this.camera = camera;
x = 0;
y = 235;
width = 228;
height = 88;
viewY = 0;
background = new ColRect(camera, x, y, 0, width, height, 1, 1, 1, 1);
body = new ColRect(camera, x + 1, y + 1, 0, width - 2, height - 2, 0, 0, 0, 1);
font = new Font(camera, "res/img/fonts/default.png");
console = new ArrayList<String>();
maxLength = (int) ((width - 2) / font.getWidth(4));
maxHeight = (int) ((height - 2) / 5);
}
public void render() {
if(error.size() > 0)
for (int i = 0; i < error.size(); i++)
font.drawString(error.get(i), x + 3, y + i * 5 + 3, 0, 4, 1, 0, 0, 1);
else
for (int i = viewY; i < console.size() && i < viewY + maxHeight; i++)
font.drawString(console.get(i), x + 3, y + (i - viewY) * 5 + 3, 0, 4, 1, 1, 1, 1);
body.render();
background.render();
}
public void printCommands(String[] commands) {
int perCommand = (int) Math.floor((maxLength - 6) / 4);
String line = new String();
for (int i = 0; i < commands.length; i++) {
String command = commands[i];
while(command.length() < perCommand)
command += ' ';
if(i != 3)
command += "| ";
line += command;
}
println(line);
}
public void println(String line) {
console.add(line);
if(console.size() > maxHeight && viewY + maxHeight + 1 == console.size())
++viewY;
}
public void clear() {
viewY = 0;
error.clear();
console.clear();
}
}

View file

@ -0,0 +1,85 @@
package com.gnarly.game.text;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_CONTROL;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_CONTROL;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_TAB;
import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_1;
import java.util.ArrayList;
import org.joml.Vector3f;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.display.Window;
import com.gnarly.engine.text.Font;
import com.gnarly.game.board.Map;
import com.gnarly.game.commands.Command;
public class EditorManager {
private Window window;
private Camera camera;
private Font font;
private StringBuilder input;
private TextEditor[] boxes;
private int focus;
public EditorManager(Window window, Camera camera) {
this.window = window;
this.camera = camera;
float x = 0, y = 33, width = 180, height = 200;
font = new Font(camera, "res/img/fonts/default.png");
input = new StringBuilder();
focus = 0;
boxes = new TextEditor[4];
boxes[0] = new TextEditor(window, camera, font, "THREAD 1", x, y, 0, width / 2f, height / 2f);
boxes[1] = new TextEditor(window, camera, font, "THREAD 2", x + width / 2 + 1, y, 0, width / 2f, height / 2f);
boxes[2] = new TextEditor(window, camera, font, "THREAD 3", x, y + height / 2 + 1, 0, width / 2f, height / 2f);
boxes[3] = new TextEditor(window, camera, font, "THREAD 4", x + width / 2 + 1, y + height / 2 + 1, 0, width / 2f, height / 2f);
}
public void update() {
if((window.getKey(GLFW_KEY_LEFT_CONTROL) >= Window.PRESSED || window.getKey(GLFW_KEY_RIGHT_CONTROL) >= Window.PRESSED) && window.getKey(GLFW_KEY_TAB) == Window.PRESSED && focus != -1) {
++focus;
if(focus == 4)
focus = 0;
for (int i = 0; i < boxes.length; i++) {
if(i == focus)
boxes[i].setActive(new Vector3f(0, 0, 0));
else
boxes[i].hideCursor();
}
}
if(window.mousePressed(GLFW_MOUSE_BUTTON_1)) {
boolean contained = false;
Vector3f mouse = window.getMouseCoords(camera);
for (int i = 0; i < boxes.length; i++) {
if(boxes[i].contains(mouse)) {
boxes[i].setActive(mouse);
focus = i;
contained = true;
}
else
boxes[i].hideCursor();
}
if(!contained)
focus = -1;
}
if(focus > -1)
boxes[focus].update();
}
public void render() {
for (int i = 0; i < boxes.length; i++)
boxes[i].render();
}
public ArrayList<Command>[] getCommands() {
ArrayList<Command>[] commands = (ArrayList<Command>[])new ArrayList[4];
for (int i = 0; i < commands.length; i++)
commands[i] = boxes[i].getCommands(i + 1);
return commands;
}
}

View file

@ -0,0 +1,42 @@
package com.gnarly.game.text;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.rects.ColRect;
import com.gnarly.engine.text.Font;
public class TextBox {
private static Font font;
private Camera camera;
private String text;
private ColRect back, body;
private float x, y, depth, width, height, textSize;
public TextBox(Camera camera, String text, float x, float y, float depth, float width, float height, float textSize) {
this.camera = camera;
this.text = text;
this.x = x;
this.y = y;
this.depth = depth;
this.width = width;
this.height = height;
this.textSize = textSize;
back = new ColRect(camera, x, y, depth, width, height, 1, 1, 1, 1);
body = new ColRect(camera, x + 1, y + 1, depth, width - 2, height - 2, 0, 0, 0, 1);
if(font == null)
font = new Font(camera, "res/img/fonts/default.png");
}
public void render() {
font.drawString(text, x + 2, y + 2, depth, textSize, 1, 1, 1, 1);
body.render();
back.render();
}
public void setText(String text) {
this.text = text;
}
}

View file

@ -0,0 +1,313 @@
package com.gnarly.game.text;
import java.util.ArrayList;
import org.joml.Vector3f;
import com.gnarly.engine.display.Camera;
import com.gnarly.engine.display.Window;
import com.gnarly.engine.rects.ColRect;
import com.gnarly.engine.text.Font;
import com.gnarly.game.commands.Command;
import com.gnarly.game.commands.Fire;
import com.gnarly.game.commands.Rotate;
import com.gnarly.game.commands.Wait;
public class TextEditor {
private static final float
HEADER_HEIGHT = 10.666666666f,
TEXT_HEIGHT = 5.333333333f;
private Window window;
private Camera camera;
private Font font;
private String name;
private ColRect background;
private ColRect header;
private ColRect body;
private float x, y, depth, width, height;
private int maxLength;
private StringBuilder[] contents;
private ColRect cursor;
private int cx, cy;
private boolean showing;
private float lastTime, blinkRate;
public TextEditor(Window window, Camera camera, Font font, String name, float x, float y, float depth, float width, float height) {
this.window = window;
this.camera = camera;
this.font = font;
this.name = name;
this.x = x;
this.y = y;
this.depth = depth;
this.width = width;
this.height = height;
background = new ColRect(camera, x, y, depth, width, height, 1, 1, 1, 1);
header = new ColRect(camera, x + 1, y + 1, depth, width - 2, HEADER_HEIGHT, 0, 0, 0, 1);
body = new ColRect(camera, x + 1, y + HEADER_HEIGHT + 2, depth, width - 2, height - HEADER_HEIGHT - 3, 0, 0, 0, 1);
maxLength = (int) ((body.getWidth() - 2) / font.getWidth(TEXT_HEIGHT));
contents = new StringBuilder[(int) ((body.getHeight() - 1) / (TEXT_HEIGHT + 1))];
for (int i = 0; i < contents.length; i++)
contents[i] = new StringBuilder();
cursor = new ColRect(camera, body.getX() + 1, body.getY() + 1, depth, font.getCharWidth(TEXT_HEIGHT), TEXT_HEIGHT, 1, 1, 1, 1);
cx = 0;
cy = 0;
showing = false;
lastTime = System.nanoTime() / 1000000f;
blinkRate = 500;
}
public void render() {
if(showing)
cursor.render();
font.drawString(name, x + 2, y + 2, depth, HEADER_HEIGHT - 2, 1, 1, 1, 1);
for (int i = 0; i < contents.length; i++)
font.drawString(contents[i].toString(), body.getX() + 1, body.getY() + i * (TEXT_HEIGHT + 1) + 1, depth, TEXT_HEIGHT, 1, 1, 1, 1);
header.render();
body.render();
background.render();
}
public void update() {
input();
cursor();
}
private void input() {
StringBuilder in = new StringBuilder(window.getInput().toUpperCase());
if(in.length() > 0) {
showing = true;
lastTime = System.nanoTime() / 1000000f;
}
for (int i = 0; i < in.length(); i++) {
char c = in.charAt(i);
if(c == 10 && cy < contents.length - 1) { // New line
if(cx == contents[cy].length()) {
++cy;
cx = 0;
}
else if(contents[contents.length - 1].length() == 0) {
for (int j = contents.length - 1; j > cy + 1; --j) {
contents[j].delete(0, contents[j].length());
contents[j].append(contents[j - 1]);
}
contents[cy + 1].delete(0, contents[cy + 1].length());
contents[cy + 1].append(contents[cy].substring(cx, contents[cy].length()));
contents[cy].delete(cx, contents[cy].length());
++cy;
cx = 0;
}
}
else if(c == 9) {
--cx;
for (int j = 0; j < 4 && cx + j <= maxLength; j++)
contents[cy].insert(++cx, (char) 32);
}
else if(c == 8) { // Backspace
if(cx > 0) { // Mid-line
contents[cy].deleteCharAt(cx - 1);
--cx;
}
else if(cy > 0) { // Beginning of line
if(contents[cy - 1].length() + contents[cy].length() <= maxLength) {
contents[cy - 1].append(contents[cy]);
for (int j = cy; j < contents.length; j++) {
contents[j].setLength(0);
if(j < contents.length - 1)
contents[j].append(contents[j + 1]);
}
}
--cy;
cx = contents[cy].length();
}
}
else if(c == 127) { // Delete
if(cx < contents[cy].length()) // Mid-line
contents[cy].deleteCharAt(cx);
else if(cy < contents.length - 1 && contents[cy].length() + contents[cy + 1].length() <= maxLength) { // End of line
contents[cy].append(contents[cy + 1]);
for (int j = cy + 1; j < contents.length; j++) {
contents[j].setLength(0);
if(j < contents.length - 1)
contents[j].append(contents[j + 1]);
}
}
}
else if(c == 1) { // Left arrow
--cx;
if(cx == -1) {
if(cy == 0)
++cx;
else {
--cy;
cx = contents[cy].length();
}
}
}
else if(c == 2) { // Right arrow
++cx;
if(cx > contents[cy].length()) {
if(cy == contents.length - 1)
--cx;
else {
++cy;
cx = 0;
}
}
}
else if(c == 3 && cy > 0) { // Up arrow
--cy;
if(cx > contents[cy].length())
cx = contents[cy].length();
}
else if(c == 4 && cy < contents.length - 1) { // Down arrow
++cy;
if(cx > contents[cy].length())
cx = contents[cy].length();
}
else if((range(c, 48, 57) || range(c, 65, 90) || c == 32) && cx < maxLength) {
contents[cy].insert(cx, c);
++cx;
}
}
if(contents[cy].length() > maxLength)
contents[cy].setLength(maxLength);
}
private void cursor() {
if(System.nanoTime() / 1000000f - lastTime > blinkRate) {
showing = !showing;
lastTime += blinkRate;
}
cursor.setPos(body.getX() + cx * font.getWidth(TEXT_HEIGHT) + 1, body.getY() + cy * (TEXT_HEIGHT + 1) + 1);
}
public boolean contains(Vector3f vector) {
return vector.x >= this.x && vector.y >= this.y && vector.x < this.x + width && vector.y < this.y + height;
}
public void setActive(Vector3f coords) {
int tempY = clampY((int) ((coords.y - body.getY()) / (TEXT_HEIGHT + 1)));
int tempX = clampX((int) ((coords.x - body.getX()) / font.getWidth(TEXT_HEIGHT)));
if(tempX > -1 && tempY > -1 && tempX <= maxLength && tempY <= contents.length) {
cx = tempX;
cy = tempY + 1;
while(cy > 0 && contents[--cy].length() == 0);
if(cx > contents[cy].length())
cx = contents[cy].length();
}
showing = true;
lastTime = System.nanoTime() / 1000000f;
}
private int clampX(int x) {
if(x < 0)
x = 0;
else if(x > contents[cy].length())
x = contents[cy].length();
return x;
}
private int clampY(int y) {
if(y < 0)
y = 0;
else if(y > contents.length - 1)
y = contents.length - 1;
return y;
}
public void hideCursor() {
showing = false;
}
private boolean range(char c, int start, int end) {
return c >= start && c <= end;
}
public ArrayList<Command> getCommands(int thread) {
ArrayList<Command> commands = new ArrayList<>();
for (int i = 0; i < contents.length; i++) {
if(contents[i].length() > 0) {
if(contents[i].length() >= 6 && contents[i].substring(0, 6).equals("ROTATE")) {
int x = 0, y = 0;
String[] split = contents[i].toString().split(" ");
if(split.length == 3) {
for (int j = 0; j < split[1].length(); j++) {
char c = split[1].charAt(j);
if(c >= 48 && c <= 57) {
x *= 10;
x += c - 48;
}
else {
Console.error.add("ROTATE REQUIRES EXACTLY 2 SPACE SEPERATED INTEGERS - THREAD: " + thread + " - LINE " + (i + 1));
j = contents[i].length();
}
}
for (int j = 0; j < split[2].length(); j++) {
char c = split[2].charAt(j);
if(c >= 48 && c <= 57) {
y *= 10;
y += c - 48;
}
else {
Console.error.add("ROTATE REQUIRES EXACTLY 2 SPACE SEPERATED INTEGERS - THREAD: " + thread + " - LINE " + (i + 1));
j = contents[i].length();
}
}
}
else {
Console.error.add("ROTATE REQUIRES EXACTLY 2 SPACE SEPERATED INTEGERS - THREAD: " + thread + " - LINE " + (i + 1));
}
if(x > 11 || y > 7)
Console.error.add("CANNOT EXCEED MAP LIMITS OF 12X8 - THREAD: " + thread + " - LINE " + (i + 1));
else
commands.add(new Rotate(x, y));
}
else if(contents[i].length() >= 4 && contents[i].substring(0, 4).equals("WAIT")) {
int num = 0;
for (int j = 5; j < contents[i].length(); ++j) {
char c = contents[i].charAt(j);
if(c >= 48 && c <= 57) {
num *= 10;
num += c - 48;
}
else if(c != 32) {
Console.error.add("WAIT CANNOT BE FOLLOWED BY NON-NUMERIC CHARACTERS - THREAD: " + thread + " - LINE " + (i + 1));
j = contents[i].length();
}
}
if(num == 0)
num = 1;
commands.add(new Wait(num));
}
else if(contents[i].length() >= 6 && contents[i].substring(0, 4).equals("FIRE")) {
int num = 0;
for (int j = 5; j < contents[i].length(); ++j) {
char c = contents[i].charAt(j);
if(c >= 48 && c <= 57) {
num *= 10;
num += c - 48;
}
else if(c != 32) {
Console.error.add("FIRE MUST BE FOLLWED BY NUMBER - THREAD: " + thread + " - LINE: " + (i + 1));
j = contents[i].length();
}
}
if(num > 99)
Console.error.add("NUMBER " + num + " IS NOT A VALID NUMBER FOR FIRE - THREAD: " + thread + " - LINE: " + (i + 1));
else
commands.add(new Fire(num));
}
else
Console.error.add("UNRECOGNIZED COMMAND: " + (contents[i].toString().length() > 25 ? (contents[i].toString().substring(0, 23) + "...") : contents[i].toString()) + " - THREAD: " + thread + " - LINE: " + (i + 1));
}
}
return commands;
}
}