diff --git a/.gitignore b/.gitignore
index 9c4de58..b83796c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,9 @@
.gradle
+/app/*.apk
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
+/app/build
\ No newline at end of file
diff --git a/app/src/androidTest/java/de/frajul/endlessroll/ApplicationTest.java b/app/src/androidTest/java/de/frajul/endlessroll/ApplicationTest.java
new file mode 100644
index 0000000..47a2898
--- /dev/null
+++ b/app/src/androidTest/java/de/frajul/endlessroll/ApplicationTest.java
@@ -0,0 +1,13 @@
+package de.frajul.endlessroll;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/assets/levelpacks/Icy Mountains.xml b/app/src/main/assets/levelpacks/Icy Mountains.xml
new file mode 100644
index 0000000..4fdba71
--- /dev/null
+++ b/app/src/main/assets/levelpacks/Icy Mountains.xml
@@ -0,0 +1,37 @@
+
+ ICY_MOUNTAINS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/assets/particleEffects/stasis.pe b/app/src/main/assets/particleEffects/stasis.pe
new file mode 100644
index 0000000..d6cf8dc
--- /dev/null
+++ b/app/src/main/assets/particleEffects/stasis.pe
@@ -0,0 +1,151 @@
+Untitled
+- Delay -
+active: false
+- Duration -
+lowMin: 1000.0
+lowMax: 1000.0
+- Count -
+min: 0
+max: 200
+- Emission -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 7.0
+highMax: 7.0
+relative: false
+scalingCount: 1
+scaling0: 1.0
+timelineCount: 1
+timeline0: 0.0
+- Life -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 1100.0
+highMax: 2000.0
+relative: false
+scalingCount: 5
+scaling0: 1.0
+scaling1: 1.0
+scaling2: 1.0
+scaling3: 1.0
+scaling4: 1.0
+timelineCount: 5
+timeline0: 0.0
+timeline1: 0.001
+timeline2: 0.002
+timeline3: 0.003
+timeline4: 1.0
+- Life Offset -
+active: false
+- X Offset -
+active: false
+- Y Offset -
+active: false
+- Spawn Shape -
+shape: square
+- Spawn Width -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 200.0
+highMax: 200.0
+relative: false
+scalingCount: 1
+scaling0: 1.0
+timelineCount: 1
+timeline0: 0.0
+- Spawn Height -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 200.0
+highMax: 200.0
+relative: false
+scalingCount: 1
+scaling0: 1.0
+timelineCount: 1
+timeline0: 0.0
+- Scale -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 60.0
+highMax: 30.0
+relative: false
+scalingCount: 4
+scaling0: 1.0
+scaling1: 0.64705884
+scaling2: 0.23529412
+scaling3: 0.0
+timelineCount: 4
+timeline0: 0.0
+timeline1: 0.8630137
+timeline2: 0.98630136
+timeline3: 1.0
+- Velocity -
+active: true
+lowMin: 0.0
+lowMax: 0.0
+highMin: 0.0
+highMax: 60.0
+relative: false
+scalingCount: 1
+scaling0: 1.0
+timelineCount: 1
+timeline0: 0.0
+- Angle -
+active: true
+lowMin: 0.0
+lowMax: 360.0
+highMin: 60.0
+highMax: 120.0
+relative: false
+scalingCount: 3
+scaling0: 1.0
+scaling1: 1.0
+scaling2: 1.0
+timelineCount: 3
+timeline0: 0.0
+timeline1: 0.56164384
+timeline2: 0.9931507
+- Rotation -
+active: false
+- Wind -
+active: false
+- Gravity -
+active: false
+- Tint -
+colorsCount: 6
+colors0: 0.047058824
+colors1: 1.0
+colors2: 0.93333334
+colors3: 0.3137255
+colors4: 0.047058824
+colors5: 1.0
+timelineCount: 2
+timeline0: 0.0
+timeline1: 1.0
+- Transparency -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 1.0
+highMax: 1.0
+relative: false
+scalingCount: 5
+scaling0: 0.0
+scaling1: 1.0
+scaling2: 1.0
+scaling3: 0.6666667
+scaling4: 0.0
+timelineCount: 5
+timeline0: 0.0
+timeline1: 0.2
+timeline2: 0.201
+timeline3: 0.9246575
+timeline4: 1.0
+- Options -
+attached: false
+continuous: true
+aligned: false
+additive: true
+behind: false
+premultipliedAlpha: false
+- Image Path -
+particle.png
diff --git a/app/src/main/assets/particleEffects/test_fire.pe b/app/src/main/assets/particleEffects/test_fire.pe
new file mode 100644
index 0000000..832c973
--- /dev/null
+++ b/app/src/main/assets/particleEffects/test_fire.pe
@@ -0,0 +1,135 @@
+Untitled
+- Delay -
+active: false
+- Duration -
+lowMin: 3000.0
+lowMax: 3000.0
+- Count -
+min: 0
+max: 200
+- Emission -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 250.0
+highMax: 250.0
+relative: false
+scalingCount: 1
+scaling0: 1.0
+timelineCount: 1
+timeline0: 0.0
+- Life -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 500.0
+highMax: 1000.0
+relative: false
+scalingCount: 3
+scaling0: 1.0
+scaling1: 1.0
+scaling2: 0.3
+timelineCount: 3
+timeline0: 0.0
+timeline1: 0.66
+timeline2: 1.0
+- Life Offset -
+active: false
+- X Offset -
+active: false
+- Y Offset -
+active: false
+- Spawn Shape -
+shape: point
+- Spawn Width -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 0.0
+highMax: 0.0
+relative: false
+scalingCount: 1
+scaling0: 1.0
+timelineCount: 1
+timeline0: 0.0
+- Spawn Height -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 0.0
+highMax: 0.0
+relative: false
+scalingCount: 1
+scaling0: 1.0
+timelineCount: 1
+timeline0: 0.0
+- Scale -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 100.0
+highMax: 100.0
+relative: false
+scalingCount: 1
+scaling0: 1.0
+timelineCount: 1
+timeline0: 0.0
+- Velocity -
+active: true
+lowMin: 0.0
+lowMax: 0.0
+highMin: 90.0
+highMax: 900.0
+relative: false
+scalingCount: 1
+scaling0: 1.0
+timelineCount: 1
+timeline0: 0.0
+- Angle -
+active: true
+lowMin: 90.0
+lowMax: 90.0
+highMin: 45.0
+highMax: 135.0
+relative: false
+scalingCount: 3
+scaling0: 1.0
+scaling1: 0.0
+scaling2: 0.0
+timelineCount: 3
+timeline0: 0.0
+timeline1: 0.5
+timeline2: 1.0
+- Rotation -
+active: false
+- Wind -
+active: false
+- Gravity -
+active: false
+- Tint -
+colorsCount: 3
+colors0: 1.0
+colors1: 0.12156863
+colors2: 0.047058824
+timelineCount: 1
+timeline0: 0.0
+- Transparency -
+lowMin: 0.0
+lowMax: 0.0
+highMin: 1.0
+highMax: 1.0
+relative: false
+scalingCount: 4
+scaling0: 0.0
+scaling1: 1.0
+scaling2: 0.75
+scaling3: 0.0
+timelineCount: 4
+timeline0: 0.0
+timeline1: 0.2
+timeline2: 0.8
+timeline3: 1.0
+- Options -
+attached: false
+continuous: true
+aligned: false
+additive: true
+behind: false
+premultipliedAlpha: false
+- Image Path -
+particle.png
diff --git a/app/src/main/java/de/frajul/endlessroll/data/Color.java b/app/src/main/java/de/frajul/endlessroll/data/Color.java
new file mode 100644
index 0000000..a947743
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/data/Color.java
@@ -0,0 +1,69 @@
+package de.frajul.endlessroll.data;
+
+import de.frajul.endlessroll.main.GameLog;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Color {
+
+ private float r, g, b;
+
+ public Color() {
+ }
+
+ public Color(float r, float g, float b) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ }
+
+ public Color(Color other) {
+ this.r = other.getR();
+ this.g = other.getG();
+ this.b = other.getB();
+ }
+
+ public Color interpolate(float leftValue, float rightValue, Color color2) {
+ float r = this.r + (color2.r - this.r) * leftValue;
+ float g = this.g + (color2.g - this.g) * leftValue;
+ float b = this.b + (color2.b - this.b) * leftValue;
+ return new Color(r, g, b);
+ }
+
+ private void mul(float a) {
+ r *= a;
+ g *= a;
+ b *= a;
+ }
+
+ private void add(Color other) {
+ r += other.getR();
+ g += other.getG();
+ b += other.getB();
+ }
+
+ public float getR() {
+ return r;
+ }
+
+ public void setR(float r) {
+ this.r = r;
+ }
+
+ public float getG() {
+ return g;
+ }
+
+ public void setG(float g) {
+ this.g = g;
+ }
+
+ public float getB() {
+ return b;
+ }
+
+ public void setB(float b) {
+ this.b = b;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/data/SynchronizedArrayList.java b/app/src/main/java/de/frajul/endlessroll/data/SynchronizedArrayList.java
new file mode 100644
index 0000000..2ca8b7d
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/data/SynchronizedArrayList.java
@@ -0,0 +1,147 @@
+package de.frajul.endlessroll.data;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Created by Julian on 16.11.2016.
+ */
+
+public class SynchronizedArrayList extends ArrayList {
+
+ @Override
+ public synchronized String toString() {
+ return super.toString();
+ }
+
+ @Override
+ public synchronized boolean add(E object) {
+ return super.add(object);
+ }
+
+ @Override
+ public synchronized void add(int index, E object) {
+ super.add(index, object);
+ }
+
+ @Override
+ public synchronized boolean addAll(Collection extends E> collection) {
+ return super.addAll(collection);
+ }
+
+ @Override
+ public synchronized boolean addAll(int index, Collection extends E> collection) {
+ return super.addAll(index, collection);
+ }
+
+ @Override
+ public synchronized void clear() {
+ super.clear();
+ }
+
+ @Override
+ public synchronized Object clone() {
+ return super.clone();
+ }
+
+ @Override
+ public synchronized void ensureCapacity(int minimumCapacity) {
+ super.ensureCapacity(minimumCapacity);
+ }
+
+ @Override
+ public synchronized E get(int index) {
+ return super.get(index);
+ }
+
+ @Override
+ public synchronized int size() {
+ return super.size();
+ }
+
+ @Override
+ public synchronized boolean isEmpty() {
+ return super.isEmpty();
+ }
+
+ @Override
+ public synchronized boolean contains(Object object) {
+ return super.contains(object);
+ }
+
+ @Override
+ public synchronized int indexOf(Object object) {
+ return super.indexOf(object);
+ }
+
+ @Override
+ public synchronized int lastIndexOf(Object object) {
+ return super.lastIndexOf(object);
+ }
+
+ @Override
+ public synchronized E remove(int index) {
+ return super.remove(index);
+ }
+
+ @Override
+ public synchronized boolean remove(Object object) {
+ return super.remove(object);
+ }
+
+ @Override
+ protected synchronized void removeRange(int fromIndex, int toIndex) {
+ super.removeRange(fromIndex, toIndex);
+ }
+
+ @Override
+ public synchronized E set(int index, E object) {
+ return super.set(index, object);
+ }
+
+ @Override
+ public synchronized Object[] toArray() {
+ return super.toArray();
+ }
+
+ @Override
+ public synchronized T[] toArray(T[] contents) {
+ return super.toArray(contents);
+ }
+
+ @Override
+ public synchronized void trimToSize() {
+ super.trimToSize();
+ }
+
+ @Override
+ public synchronized int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public synchronized boolean equals(Object o) {
+ return super.equals(o);
+ }
+
+ @Override
+ public synchronized List subList(int start, int end) {
+ return super.subList(start, end);
+ }
+
+ @Override
+ public synchronized boolean containsAll(Collection> collection) {
+ return super.containsAll(collection);
+ }
+
+ @Override
+ public synchronized boolean removeAll(Collection> collection) {
+ return super.removeAll(collection);
+ }
+
+ @Override
+ public synchronized boolean retainAll(Collection> collection) {
+ return super.retainAll(collection);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/data/Vector.java b/app/src/main/java/de/frajul/endlessroll/data/Vector.java
new file mode 100644
index 0000000..1fbbdb9
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/data/Vector.java
@@ -0,0 +1,101 @@
+package de.frajul.endlessroll.data;
+
+/**
+ * Created by Julian on 01.12.2015.
+ */
+public class Vector {
+
+ public float x, y;
+
+ public Vector() {
+ this(0, 0);
+ }
+
+ public Vector(Vector other) {
+ this(other.x, other.y);
+ }
+
+ public Vector(float x, float y) {
+ set(x, y);
+ }
+
+ @Override
+ public String toString() {
+ return "Vector(" + x + ", " + y + ")";
+ }
+
+ public Vector set(float x, float y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ }
+
+ public float length() {
+ return (float) Math.sqrt(x * x + y * y);
+ }
+
+ public Vector normalize() {
+ float length = this.length();
+ x /= length;
+ y /= length;
+ return this;
+ }
+
+ public Vector translate(Vector other) {
+ this.x += other.x;
+ this.y += other.y;
+ return this;
+ }
+
+ public Vector translate(float x, float y) {
+ this.x += x;
+ this.y += y;
+ return this;
+ }
+
+ public Vector mul(float z) {
+ x *= z;
+ y *= z;
+ return this;
+ }
+
+ public Vector mul(Vector other) {
+ this.x *= other.x;
+ this.y *= other.y;
+ return this;
+ }
+
+ public Vector mul(float x, float y) {
+ this.x *= x;
+ this.y *= y;
+ return this;
+ }
+
+ public Vector negate() {
+ this.x *= -1;
+ this.y *= -1;
+ return this;
+ }
+
+ public Vector vectorTo(Vector other) {
+ float x = other.x - this.x;
+ float y = other.y - this.y;
+ return new Vector(x, y);
+ }
+
+ public void setX(float x) {
+ this.x = x;
+ }
+
+ public void setY(float y) {
+ this.y = y;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/AnimatedEntity.java b/app/src/main/java/de/frajul/endlessroll/entities/AnimatedEntity.java
new file mode 100644
index 0000000..bf85321
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/AnimatedEntity.java
@@ -0,0 +1,25 @@
+package de.frajul.endlessroll.entities;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.textures.Texture;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 20.02.2017.
+ */
+
+public class AnimatedEntity extends Entity {
+
+ protected Animation animation;
+
+ public AnimatedEntity(Texture texture, Vector position, float width, float height) {
+ super(texture, position, width, height);
+ animation = new Animation();
+ }
+
+ public void update(Timer timer){
+ animation.update(timer);
+ super.setTextureAtlasIndex(animation.getCurrentTexIndex());
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/Animation.java b/app/src/main/java/de/frajul/endlessroll/entities/Animation.java
new file mode 100644
index 0000000..97c6171
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/Animation.java
@@ -0,0 +1,55 @@
+package de.frajul.endlessroll.entities;
+
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 14.12.2015.
+ */
+public class Animation {
+
+ private int[] indexSequence = {0, 1, 2, 3};
+ private boolean looping = false;
+ private int requiredDelta = 110;
+
+ private int indexPointer = 0;
+ private boolean stillRunning = true;
+ private long lastSwitchTime = -1;
+
+ public void disable(){
+ stillRunning = false;
+ }
+
+ public void update(Timer timer) {
+ if (lastSwitchTime == -1) {
+ lastSwitchTime = timer.getCurrentTime();
+ return;
+ }
+ if (stillRunning) {
+ long delta = timer.getCurrentTime() - lastSwitchTime;
+ if (delta >= requiredDelta) {
+ lastSwitchTime = timer.getCurrentTime();
+ indexPointer++;
+ if (!looping && indexPointer == indexSequence.length - 1)
+ stillRunning = false;
+ if (looping && indexPointer == indexSequence.length)
+ indexPointer = 0;
+ }
+ }
+ }
+
+ public int getCurrentTexIndex() {
+ return indexSequence[indexPointer];
+ }
+
+ public void setIndexSequence(int[] indexSequence) {
+ this.indexSequence = indexSequence;
+ }
+
+ public void setRequiredDelta(int requiredDelta) {
+ this.requiredDelta = requiredDelta;
+ }
+
+ public void setLooping(boolean looping) {
+ this.looping = looping;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/Background.java b/app/src/main/java/de/frajul/endlessroll/entities/Background.java
new file mode 100644
index 0000000..dbdd971
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/Background.java
@@ -0,0 +1,56 @@
+package de.frajul.endlessroll.entities;
+
+import de.frajul.endlessroll.data.SynchronizedArrayList;
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.textures.Texture;
+
+/**
+ * Created by Julian on 20.07.2016.
+ */
+public class Background extends SynchronizedArrayList {
+
+ private final float PART_WIDTH = 5;
+ private final float HALF_PART_WIDTH = PART_WIDTH / 2;
+ private final float HEIGHT = 2.5f;
+ private Texture texture;
+
+ public Background(Texture texture) {
+ this.texture = texture;
+ super.add(createPart(-HALF_PART_WIDTH));
+ }
+
+ public void changeTexture(Texture texture) {
+ this.texture = texture;
+ synchronized (this) {
+ for (Entity entity : this)
+ entity.setTexture(texture);
+ }
+ }
+
+ private Entity createPart(float xLeftEdge) {
+ return new Entity(texture, new Vector(xLeftEdge + HALF_PART_WIDTH, (HEIGHT - 2) / 2), PART_WIDTH, HEIGHT);
+ }
+
+ public void move(float x, float cameraX) {
+ Vector movement = new Vector(x, 0);
+ synchronized (this) {
+ for (Entity part : this)
+ part.move(movement);
+ }
+ if (!super.isEmpty()) {
+ Entity last = super.get(super.size() - 1);
+ if (last.getRightEdge() - cameraX < 3) {
+ super.add(createPart(last.getRightEdge() - 0.001f));
+ }
+ if (super.get(0).getRightEdge() - cameraX < -3) {
+ super.remove(0);
+ }
+ }
+ }
+
+ public void resetPosition() {
+ super.clear();
+ super.add(createPart(-HALF_PART_WIDTH));
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/DestroyEffect.java b/app/src/main/java/de/frajul/endlessroll/entities/DestroyEffect.java
new file mode 100644
index 0000000..297479e
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/DestroyEffect.java
@@ -0,0 +1,39 @@
+package de.frajul.endlessroll.entities;
+
+import android.support.annotation.Nullable;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.particles.ParticleEffect;
+import de.frajul.endlessroll.entities.particles.ParticleSource;
+import de.frajul.endlessroll.entities.particles.ParticleSystem;
+
+/**
+ * Created by Julian on 11.03.2016.
+ */
+public enum DestroyEffect {
+
+ EXPLOSION, STAR_EXPLOSION, ENERGY_COLLECT, NONE;
+
+ @Nullable
+ public ParticleSource createEffect(ParticleSystem system, Vector position, Vector size) {
+ if (this == NONE)
+ return null;
+ ParticleSource source = new ParticleSource(position, getEffect(system));
+ source.setSpawnSize(size);
+ return source;
+ }
+
+ private ParticleEffect getEffect(ParticleSystem system) {
+ switch (this) {
+ case EXPLOSION:
+ return system.explosion;
+ case STAR_EXPLOSION:
+ return system.starCollect;
+ case ENERGY_COLLECT:
+ return system.energyCollect;
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/Entity.java b/app/src/main/java/de/frajul/endlessroll/entities/Entity.java
new file mode 100644
index 0000000..6e859e9
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/Entity.java
@@ -0,0 +1,113 @@
+package de.frajul.endlessroll.entities;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.collision.geometry.Quad;
+import de.frajul.endlessroll.entities.textures.Texture;
+
+/**
+ * Created by Julian on 20.11.2015.
+ */
+public class Entity extends Quad {
+
+ private int textureAtlasIndex;
+ private Texture texture;
+ private Vector movement;
+ private float rotation;
+ private float alpha = 1.0f;
+ private boolean destroyed;
+ private DestroyEffect destroyEffect;
+ private Vector maxTexSize;
+ private boolean visible = true;
+
+ public Entity(Texture texture, Vector position, float width, float height) {
+ super(position, width, height);
+ this.texture = texture;
+ this.movement = new Vector();
+ this.maxTexSize = new Vector();
+ }
+
+ public void move(Vector movement) {
+ position.translate(movement);
+ }
+
+ public void rotate(float factor) {
+ rotation += factor;
+ }
+
+ public void setToTerrain(float terrainEdge) {
+ position.y = terrainEdge + height / 2;
+ }
+
+ public void setTopEdge(float y) {
+ position.y = y - height / 2;
+ }
+
+ public Texture getTexture() {
+ return texture;
+ }
+
+ public void setTexture(Texture texture) {
+ this.texture = texture;
+ }
+
+ public Vector getMovement() {
+ return movement;
+ }
+
+ public void setMovement(Vector movement) {
+ this.movement = movement;
+ }
+
+ public float getRotation() {
+ return rotation;
+ }
+
+ public void setRotation(float rotation) {
+ this.rotation = rotation;
+ }
+
+ public void setAlpha(float alpha) {
+ this.alpha = alpha;
+ }
+
+ public float getAlpha() {
+ return alpha;
+ }
+
+ public void destroy(DestroyEffect destroyEffect) {
+ this.destroyed = true;
+ this.destroyEffect = destroyEffect;
+ }
+
+ public boolean isDestroyed() {
+ return destroyed;
+ }
+
+ public DestroyEffect getDestroyEffect() {
+ return destroyEffect;
+ }
+
+ public void setTextureAtlasIndex(int textureAtlasIndex) {
+ this.textureAtlasIndex = textureAtlasIndex;
+ }
+
+ public int getTextureAtlasIndex() {
+ return textureAtlasIndex;
+ }
+
+ public void setMaxTexSize(Vector maxTexSize) {
+ this.maxTexSize = maxTexSize;
+ }
+
+ public Vector getMaxTexSize() {
+ return maxTexSize;
+ }
+
+ public void setVisible(boolean visible) {
+ this.visible = visible;
+ }
+
+ public boolean isVisible() {
+ return visible;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/Goal.java b/app/src/main/java/de/frajul/endlessroll/entities/Goal.java
new file mode 100644
index 0000000..ae5dc5b
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/Goal.java
@@ -0,0 +1,16 @@
+package de.frajul.endlessroll.entities;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.textures.Texture;
+
+public class Goal extends Entity {
+
+ public Goal(Texture texture) {
+ super(texture, new Vector(), 0.1f, 3);
+ super.getMaxTexSize().setY(0.2f);
+ }
+
+ public void setGoalX(float goalX) {
+ super.setPosition(new Vector(goalX - 0.05f, 0));
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/Player.java b/app/src/main/java/de/frajul/endlessroll/entities/Player.java
new file mode 100644
index 0000000..8ce4840
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/Player.java
@@ -0,0 +1,130 @@
+package de.frajul.endlessroll.entities;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.particles.ParticleSource;
+import de.frajul.endlessroll.entities.particles.ParticleSystem;
+import de.frajul.endlessroll.entities.shapes.PlayerShape;
+import de.frajul.endlessroll.entities.tools.PlayerInfluenceTool;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 20.11.2015.
+ */
+public class Player extends Entity {
+
+ private final float ROTATION_SPEED = -400;
+ public final float RADIUS = 0.1f;
+ private final float START_X = -0.9f;
+ private final float SPEED = 0.002f;
+
+ private float startSpeed, endSpeed;
+ private float speed = SPEED;
+
+ private long currentSuperPowerDuration;
+ private boolean hasSuperPower;
+ private long superPowerDuration;
+ private ParticleSource superPowerParticles;
+
+ private ParticleSystem particleSystem;
+ private List influenceTools = new ArrayList<>();
+ private List forces = new ArrayList<>();
+ private float gravityForce;
+
+ public Player() {
+ super(PlayerShape.BALL.getTexture(), new Vector(), 0, 0);
+ super.setWidth(RADIUS * 2);
+ super.setHeight(RADIUS * 2);
+ }
+
+ public void startSuperPower(long duration) {
+ this.superPowerDuration = duration;
+ currentSuperPowerDuration = 0;
+ hasSuperPower = true;
+ superPowerParticles = new ParticleSource(
+ new Vector(super.getPosition().x, super.getPosition().y), particleSystem.superPower);
+ superPowerParticles.start();
+ }
+
+ public void init(PlayerShape playerShape, float terrainEdge, float startSpeed, float endSpeed, ParticleSystem particleSystem) {
+ super.setTexture(playerShape.getTexture());
+ super.setToTerrain(terrainEdge);
+ super.getPosition().x = START_X;
+ super.setMovement(new Vector(speed, 0));
+ this.startSpeed = startSpeed;
+ this.endSpeed = endSpeed;
+ setSpeedByProgress(0);
+ this.particleSystem = particleSystem;
+ }
+
+ public void setSpeedByProgress(float progress) {
+ this.speed = ((endSpeed - startSpeed) * progress + startSpeed) * SPEED;
+ super.getMovement().x = speed;
+ }
+
+ public void preMoveUpdate(Timer timer) {
+ if (hasSuperPower && superPowerParticles != null) {
+ currentSuperPowerDuration += timer.getFrameTimeSeconds();
+ hasSuperPower = superPowerDuration >= currentSuperPowerDuration;
+ superPowerParticles.setPosition(new Vector(super.getPosition()));
+ }
+ if (!hasSuperPower && superPowerParticles != null)
+ superPowerParticles.kill();
+
+ for (PlayerInfluenceTool tool : influenceTools)
+ tool.influencePlayerValues(this);
+
+ for (float force : forces)
+ super.getMovement().y += force;
+ super.getMovement().y += gravityForce;
+ }
+
+ public void clearAllForces() {
+ forces.clear();
+ gravityForce = 0;
+ }
+
+ public void manipulateAllForces(float factor) {
+ for (int i = 0; i < forces.size(); i++)
+ forces.set(i, forces.get(i) * factor);
+ gravityForce *= factor * factor;
+ }
+
+ public void postMoveUpdate() {
+ influenceTools.clear();
+ forces.clear();
+ }
+
+ public float getProgress() {
+ return getPosition().x - START_X;
+ }
+
+ public float getSpeed() {
+ return speed;
+ }
+
+ public boolean hasSuperPower() {
+ return hasSuperPower;
+ }
+
+ public void addInfluenceTool(PlayerInfluenceTool tool) {
+ influenceTools.add(tool);
+ }
+
+ public void addForce(float force) {
+ forces.add(force);
+ }
+
+ public void setGravityForce(float gravityForce) {
+ this.gravityForce = gravityForce;
+ }
+
+ @Override
+ public void move(Vector movement) {
+ super.move(movement);
+ rotate(movement.x * ROTATION_SPEED);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/Vertex.java b/app/src/main/java/de/frajul/endlessroll/entities/Vertex.java
new file mode 100644
index 0000000..34ce49a
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/Vertex.java
@@ -0,0 +1,44 @@
+package de.frajul.endlessroll.entities;
+
+import de.frajul.endlessroll.main.GameLog;
+
+/**
+ * Created by Julian on 11.12.2016.
+ */
+
+public enum Vertex {
+
+ UL(0, -1, 1), UR(1, 1, 1), BL(3, -1, -1), BR(2, 1, -1);
+
+ private int index;
+ private float x, y;
+
+ Vertex(int index, float x, float y) {
+ this.index = index;
+ this.x = x;
+ this.y = y;
+ }
+
+ public Vertex getNext(boolean clockwise) {
+ int newIndex = index + 1;
+ if (!clockwise)
+ newIndex = index - 1;
+ if (newIndex > 3)
+ newIndex = 0;
+ if (newIndex < 0)
+ newIndex = 3;
+ for (Vertex vertex : values())
+ if (vertex.index == newIndex)
+ return vertex;
+ GameLog.e("No vertex with index " + index + " found!!!");
+ return null;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collectables/Energy.java b/app/src/main/java/de/frajul/endlessroll/entities/collectables/Energy.java
new file mode 100644
index 0000000..4610e26
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collectables/Energy.java
@@ -0,0 +1,17 @@
+package de.frajul.endlessroll.entities.collectables;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.AnimatedEntity;
+import de.frajul.endlessroll.entities.textures.Texture;
+import de.frajul.endlessroll.levels.PositionData;
+
+public class Energy extends AnimatedEntity {
+
+ private final static float SIZE = 0.3f;
+
+ public Energy(Texture texture, PositionData data) {
+ super(texture, new Vector(data.getX(), data.getY()), SIZE, SIZE);
+ animation.setLooping(true);
+ animation.setRequiredDelta(130);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collectables/Star.java b/app/src/main/java/de/frajul/endlessroll/entities/collectables/Star.java
new file mode 100644
index 0000000..b4acd5d
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collectables/Star.java
@@ -0,0 +1,21 @@
+package de.frajul.endlessroll.entities.collectables;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.entities.textures.Texture;
+import de.frajul.endlessroll.levels.PositionData;
+
+public class Star extends Entity {
+
+ private final static float SIZE = 0.25f;
+ private int index;
+
+ public Star(int index, Texture texture, PositionData data) {
+ super(texture, new Vector(data.getX(), data.getY()), SIZE, SIZE);
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/CircleTriangleCollisionDetector.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/CircleTriangleCollisionDetector.java
new file mode 100644
index 0000000..19fadbd
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/CircleTriangleCollisionDetector.java
@@ -0,0 +1,78 @@
+package de.frajul.endlessroll.entities.collision;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.collision.geometry.Circle;
+import de.frajul.endlessroll.entities.collision.geometry.Triangle;
+
+/**
+ * Created by Julian on 28.02.2016.
+ */
+public class CircleTriangleCollisionDetector {
+
+ public boolean circleTriangleCollision(Circle circle, Triangle triangle) {
+ if (circleIntersectingWithTriangleVertices(circle, triangle))
+ return true;
+
+ if (circleCenterInsideTriangle(circle, triangle))
+ return true;
+
+ if (circleIntersectingWithTriangleEdges(circle, triangle))
+ return true;
+ return false;
+ }
+
+ private boolean circleIntersectingWithTriangleEdges(Circle circle, Triangle triangle) {
+ Vector edge1 = triangle.getVertex1().vectorTo(triangle.getVertex2());
+ Vector edge2 = triangle.getVertex2().vectorTo(triangle.getVertex3());
+ Vector edge3 = triangle.getVertex3().vectorTo(triangle.getVertex1());
+
+ boolean intersectingWithEdge1 = circleIntersectingWithTriangleEdge(circle, triangle.getVertex1(), edge1);
+ boolean intersectingWithEdge2 = circleIntersectingWithTriangleEdge(circle, triangle.getVertex2(), edge2);
+ boolean intersectingWithEdge3 = circleIntersectingWithTriangleEdge(circle, triangle.getVertex3(), edge3);
+ return intersectingWithEdge1 || intersectingWithEdge2 || intersectingWithEdge3;
+ }
+
+ private boolean circleIntersectingWithTriangleEdge(Circle circle, Vector vertex, Vector edge) {
+ Vector vertexToCenter = vertex.vectorTo(circle.getCenter());
+ Vector kVector = new Vector(vertexToCenter).mul(edge);
+ float k = kVector.x + kVector.y;
+
+ if (k > 0) {
+ float length = edge.length();
+ k = k / length;
+
+ if (k < length)
+ if (Math.sqrt(vertexToCenter.x * vertexToCenter.x + vertexToCenter.y * vertexToCenter.y - k * k) <= circle.getRadius())
+ return true;
+ }
+ return false;
+ }
+
+ private boolean circleCenterInsideTriangle(Circle circle, Triangle triangle) {
+ Vector vertex1To2 = triangle.getVertex1().vectorTo(triangle.getVertex2());
+ Vector vertex2To3 = triangle.getVertex2().vectorTo(triangle.getVertex3());
+ Vector vertex3To1 = triangle.getVertex3().vectorTo(triangle.getVertex1());
+ Vector vertex1ToCenter = triangle.getVertex1().vectorTo(circle.getCenter());
+ Vector vertex2ToCenter = triangle.getVertex2().vectorTo(circle.getCenter());
+ Vector vertex3ToCenter = triangle.getVertex3().vectorTo(circle.getCenter());
+
+ boolean centerInsideV1V2 = vertex1To2.y * vertex1ToCenter.x - vertex1To2.x * vertex1ToCenter.y < 0;
+ boolean centerInsideV2V3 = vertex2To3.y * vertex2ToCenter.x - vertex2To3.x * vertex2ToCenter.y < 0;
+ boolean centerInsideV3V1 = vertex3To1.y * vertex3ToCenter.x - vertex3To1.x * vertex3ToCenter.y < 0;
+
+ return centerInsideV1V2 && centerInsideV2V3 && centerInsideV3V1;
+ }
+
+ private boolean circleIntersectingWithTriangleVertices(Circle circle, Triangle triangle) {
+ boolean intersectingWithVertex1 = circleIntersectingWithTriangleVertex(circle, triangle.getVertex1());
+ boolean intersectingWithVertex2 = circleIntersectingWithTriangleVertex(circle, triangle.getVertex2());
+ boolean intersectingWithVertex3 = circleIntersectingWithTriangleVertex(circle, triangle.getVertex3());
+ return intersectingWithVertex1 || intersectingWithVertex2 || intersectingWithVertex3;
+ }
+
+ private boolean circleIntersectingWithTriangleVertex(Circle circle, Vector vertex) {
+ Vector centerToVertex = circle.getCenter().vectorTo(vertex);
+ return centerToVertex.length() <= circle.getRadius();
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/CollisionDetector.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/CollisionDetector.java
new file mode 100644
index 0000000..17458e6
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/CollisionDetector.java
@@ -0,0 +1,205 @@
+package de.frajul.endlessroll.entities.collision;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collision.collisionData.EntityCollisionData;
+import de.frajul.endlessroll.entities.collision.geometry.Circle;
+import de.frajul.endlessroll.entities.collision.geometry.Quad;
+import de.frajul.endlessroll.entities.collision.geometry.Triangle;
+
+/**
+ * Created by Julian on 01.12.2015.
+ */
+public class CollisionDetector {
+
+ private CircleTriangleCollisionDetector triangleDetector;
+
+ public CollisionDetector() {
+ triangleDetector = new CircleTriangleCollisionDetector();
+ }
+
+ public boolean isCircleCircleCollision(Circle c1, Circle c2) {
+ float distance = Math.abs(c1.getCenter().vectorTo(c2.getCenter()).length());
+ float radiusSum = c1.getRadius() + c2.getRadius();
+ return distance < radiusSum;
+ }
+
+ public boolean isCircleQuadCollision(Circle circle, Quad quad) {
+ Vector distance = circle.getCenter().vectorTo(quad.getPosition());
+ distance.x = Math.abs(distance.x);
+ distance.y = Math.abs(distance.y);
+
+ if (distance.x > circle.getRadius() + quad.getWidth() / 2)
+ return false;
+ if (distance.y > circle.getRadius() + quad.getHeight() / 2)
+ return false;
+
+ if (distance.x <= quad.getWidth() / 2)
+ return true;
+ if (distance.y <= quad.getHeight() / 2)
+ return true;
+
+ float cornerDistance_sq = (distance.x - quad.getWidth() / 2) * (distance.x - quad
+ .getWidth() / 2) + (distance.y - quad.getHeight() / 2) * (distance.y - quad
+ .getHeight() / 2);
+ return cornerDistance_sq <= circle.getRadius() * circle.getRadius();
+ }
+
+ public boolean isCircleTriangleCollision(Circle circle, Triangle triangle) {
+ return triangleDetector.circleTriangleCollision(circle, triangle);
+ }
+
+ public boolean isQuadQuadCollision(Quad q1, Quad q2) {
+ float xDistance = Math.abs(q2.getPosition().x - q1.getPosition().x);
+ float yDistance = Math.abs(q2.getPosition().y - q1.getPosition().y);
+ if (xDistance >= q1.getWidth() / 2 + q2.getWidth() / 2)
+ return false;
+ if (yDistance >= q1.getHeight() / 2 + q2.getHeight() / 2)
+ return false;
+ return true;
+ }
+
+ public boolean isQuadTriangleCollision(Quad quad, Triangle triangle) {
+ boolean quadVertex1InTriangle = isVertexInTriangle(
+ new Vector(quad.getLeftEdge(), quad.getTopEdge()), triangle);
+ if (quadVertex1InTriangle)
+ return true;
+ boolean quadVertex2InTriangle = isVertexInTriangle(
+ new Vector(quad.getRightEdge(), quad.getTopEdge()), triangle);
+ if (quadVertex2InTriangle)
+ return true;
+ boolean quadVertex3InTriangle = isVertexInTriangle(
+ new Vector(quad.getRightEdge(), quad.getBottomEdge()), triangle);
+ if (quadVertex3InTriangle)
+ return true;
+ boolean quadVertex4InTriangle = isVertexInTriangle(
+ new Vector(quad.getLeftEdge(), quad.getBottomEdge()), triangle);
+ if (quadVertex4InTriangle)
+ return true;
+ boolean triangleVertex1InQuad = isVertexInQuad(triangle.getVertex1(), quad);
+ if (triangleVertex1InQuad)
+ return true;
+ boolean triangleVertex2InQuad = isVertexInQuad(triangle.getVertex2(), quad);
+ if (triangleVertex2InQuad)
+ return true;
+ boolean triangleVertex3InQuad = isVertexInQuad(triangle.getVertex3(), quad);
+ if (triangleVertex3InQuad)
+ return true;
+ return false;
+ }
+
+ public boolean isTriangleTriangleCollision(Triangle triangle1, Triangle triangle2) {
+ boolean triangle1Vertex1InTriangle2 = isVertexInTriangle(triangle1.getVertex1(), triangle2);
+ if (triangle1Vertex1InTriangle2)
+ return true;
+ boolean triangle1Vertex2InTriangle2 = isVertexInTriangle(triangle1.getVertex2(), triangle2);
+ if (triangle1Vertex2InTriangle2)
+ return true;
+ boolean triangle1Vertex3InTriangle2 = isVertexInTriangle(triangle1.getVertex3(), triangle2);
+ if (triangle1Vertex3InTriangle2)
+ return true;
+ boolean triangle2Vertex1InTriangle1 = isVertexInTriangle(triangle2.getVertex1(), triangle1);
+ if (triangle2Vertex1InTriangle1)
+ return true;
+ boolean triangle2Vertex2InTriangle1 = isVertexInTriangle(triangle2.getVertex2(), triangle1);
+ if (triangle2Vertex2InTriangle1)
+ return true;
+ boolean triangle2Vertex3InTriangle1 = isVertexInTriangle(triangle2.getVertex3(), triangle1);
+ if (triangle2Vertex3InTriangle1)
+ return true;
+ return false;
+ }
+
+
+ private boolean isVertexInTriangle(Vector vertex, Triangle triangle) {
+ Vector vertex1To2 = triangle.getVertex1().vectorTo(triangle.getVertex2());
+ Vector vertex2To3 = triangle.getVertex2().vectorTo(triangle.getVertex3());
+ Vector vertex3To1 = triangle.getVertex3().vectorTo(triangle.getVertex1());
+ Vector vertex1ToCenter = triangle.getVertex1().vectorTo(vertex);
+ Vector vertex2ToCenter = triangle.getVertex2().vectorTo(vertex);
+ Vector vertex3ToCenter = triangle.getVertex3().vectorTo(vertex);
+
+ boolean centerInsideV1V2 = vertex1To2.y * vertex1ToCenter.x - vertex1To2.x * vertex1ToCenter.y < 0;
+ boolean centerInsideV2V3 = vertex2To3.y * vertex2ToCenter.x - vertex2To3.x * vertex2ToCenter.y < 0;
+ boolean centerInsideV3V1 = vertex3To1.y * vertex3ToCenter.x - vertex3To1.x * vertex3ToCenter.y < 0;
+
+ return centerInsideV1V2 && centerInsideV2V3 && centerInsideV3V1;
+ }
+
+ private boolean isVertexInQuad(Vector vertex, Quad quad) {
+ return vertex.getX() >= quad.getLeftEdge() && vertex.getX() <= quad.getRightEdge() && vertex
+ .getY() >= quad.getBottomEdge() && vertex.getY() <= quad.getTopEdge();
+ }
+
+
+ public EntityCollisionData playerEntityCollision(Player player, Entity entity) {
+ Circle circle = new Circle(player);
+ Quad quad = entity;
+ if (isCircleQuadCollision(circle, quad)) {
+ Edge edge = circleQuadCollisionEdge(circle, player.getMovement(), quad,
+ entity.getMovement());
+ return new EntityCollisionData(entity, edge);
+ }
+ return new EntityCollisionData(null, null);
+ }
+
+ private Edge circleQuadCollisionEdge(Circle circle, Vector circleMovement, Quad quad, Vector quadMovement) {
+ Vector relativeMovement = circleMovement.translate(quadMovement);
+
+ //LEFT || TOP || BOTTOM
+ if (relativeMovement.x > 0) {
+ //LEFT || BOTTOM
+ if (relativeMovement.y > 0) {
+ float toLeftDistance = quad.getLeftEdge() - circle.getCenter().x;
+ float actualY = toLeftDistance * (relativeMovement.y / relativeMovement.x) + circle
+ .getCenter().y;
+ if (actualY < quad.getBottomEdge())
+ return Edge.BOTTOM;
+ else
+ return Edge.LEFT;
+ }
+ //LEFT || TOP
+ else if (relativeMovement.y < 0) {
+ float toLeftDistance = quad.getLeftEdge() - circle.getCenter().x;
+ float actualY = toLeftDistance * (relativeMovement.y / relativeMovement.x) + circle
+ .getCenter().y;
+ if (actualY < quad.getTopEdge())
+ return Edge.LEFT;
+ else
+ return Edge.TOP;
+ } else
+ return Edge.LEFT;
+ }
+ //RIGHT || TOP || BOTTOM
+ else if (relativeMovement.x < 0) {
+ //RIGHT || BOTTOM
+ if (relativeMovement.y > 0) {
+ float toRightDistance = quad.getRightEdge() - circle.getCenter().x;
+ float actualY = toRightDistance * (relativeMovement.y / relativeMovement.x) + circle
+ .getCenter().y;
+ if (actualY < quad.getBottomEdge())
+ return Edge.BOTTOM;
+ else
+ return Edge.RIGHT;
+ }
+ //RIGHT || TOP
+ else if (relativeMovement.y < 0) {
+ float toRightDistance = quad.getRightEdge() - circle.getCenter().x;
+ float actualY = toRightDistance * (relativeMovement.y / relativeMovement.x) + circle
+ .getCenter().y;
+ if (actualY < quad.getTopEdge())
+ return Edge.RIGHT;
+ else
+ return Edge.TOP;
+ } else
+ return Edge.RIGHT;
+ } else {
+ if (relativeMovement.y > 0)
+ return Edge.BOTTOM;
+ else if (relativeMovement.y < 0)
+ return Edge.TOP;
+ }
+ return Edge.NONE;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/CollisionManager.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/CollisionManager.java
new file mode 100644
index 0000000..25839a7
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/CollisionManager.java
@@ -0,0 +1,104 @@
+package de.frajul.endlessroll.entities.collision;
+
+import de.frajul.endlessroll.entities.DestroyEffect;
+import de.frajul.endlessroll.entities.Obstacle;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collectables.Energy;
+import de.frajul.endlessroll.entities.collectables.Star;
+import de.frajul.endlessroll.entities.collision.collisionData.EntityCollisionData;
+import de.frajul.endlessroll.entities.collision.collisionData.ObstacleCollisionData;
+import de.frajul.endlessroll.entities.collision.collisionData.PlayerCollisionData;
+import de.frajul.endlessroll.entities.collision.collisionData.ToolCollisionData;
+import de.frajul.endlessroll.entities.tools.Tool;
+import de.frajul.endlessroll.main.game.Game;
+import de.frajul.endlessroll.main.game.GameScene;
+import de.frajul.endlessroll.main.game.Timer;
+import de.frajul.endlessroll.main.physics.Physics;
+
+/**
+ * Created by Julian on 02.01.2016.
+ */
+public class CollisionManager {
+
+ private Game game;
+ private Player player;
+ private Timer timer;
+
+ public CollisionManager(Game game) {
+ this.game = game;
+ }
+
+ //Check Obstacle always before tool, because at tool, player could be moved etc.
+ public void update(Physics physics, GameScene scene, Timer timer) {
+ physics.checkToolCollision(scene);
+
+ this.player = scene.getPlayer();
+ this.timer = timer;
+ PlayerCollisionData data = physics.getPlayerCollisionData(scene);
+ if (data.isTerrainCollision())
+ checkTerrainCollision(data.getTerrainCollisionData());
+ if (data.isCeilingCollision())
+ checkCeilingCollision(data.getCeilingCollisionData());
+ if (data.isObstacleCollision())
+ checkObstacleCollision(data.getObstacleCollisionData(), player.hasSuperPower());
+ if (data.isToolCollision())
+ checkToolCollision(data.getToolCollisionData());
+ if (data.isStarCollision())
+ game.onStarCollision((Star) data.getStarCollisionData().getEntity());
+ if (data.isEnergyCollision())
+ game.onEnergyCollision((Energy) data.getEnergyCollisionData().getEntity());
+ }
+
+ private void checkTerrainCollision(EntityCollisionData data) {
+ checkEntityCollision(data, -0.01f);
+ if (data.getEdge() != Edge.TOP)
+ game.onGameOver(true);
+ }
+
+ private void checkCeilingCollision(EntityCollisionData data) {
+ checkEntityCollision(data, -0.01f);
+ if (data.getEdge() == Edge.RIGHT || data.getEdge() == Edge.LEFT)
+ game.onGameOver(true);
+ }
+
+ private void checkToolCollision(ToolCollisionData data) {
+ for (Tool tool : data.getTools())
+ tool.onPlayerCollision(player, timer);
+ }
+
+ private void checkObstacleCollision(ObstacleCollisionData data, boolean playerHasSuperpower) {
+ for (EntityCollisionData entityData : data.getCollisions()) {
+ if (playerHasSuperpower) {
+ entityData.getEntity().destroy(DestroyEffect.EXPLOSION);
+ } else {
+ if (entityData.getEdge() != Edge.TOP || ((Obstacle) entityData.getEntity())
+ .isDeadly())
+ game.onGameOver(true);
+ else
+ checkEntityCollision(entityData, 0);
+ }
+ }
+ }
+
+ private void checkEntityCollision(EntityCollisionData data, float xBounceFactor) {
+ switch (data.getEdge()) {
+ case TOP:
+ player.getMovement().y = 0;
+ player.setToTerrain(data.getEntity().getTopEdge());
+ break;
+ case BOTTOM:
+ player.getMovement().y = 0;
+ player.setTopEdge(data.getEntity().getBottomEdge());
+ break;
+ case LEFT:
+ player.getMovement().x *= xBounceFactor;
+ player.getPosition().x = data.getEntity().getLeftEdge() - player.RADIUS;
+ break;
+ case RIGHT:
+ player.getMovement().x *= xBounceFactor;
+ player.getPosition().x = data.getEntity().getRightEdge() + player.RADIUS;
+ break;
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/Edge.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/Edge.java
new file mode 100644
index 0000000..2fdf3d8
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/Edge.java
@@ -0,0 +1,5 @@
+package de.frajul.endlessroll.entities.collision;
+
+public enum Edge {
+ LEFT, RIGHT, TOP, BOTTOM, NONE;
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/EntityCollisionData.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/EntityCollisionData.java
new file mode 100644
index 0000000..3cdb104
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/EntityCollisionData.java
@@ -0,0 +1,30 @@
+package de.frajul.endlessroll.entities.collision.collisionData;
+
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.entities.collision.Edge;
+
+/**
+ * Created by Julian on 01.01.2016.
+ */
+public class EntityCollisionData {
+
+ private Entity entity;
+ private Edge edge;
+
+ public EntityCollisionData(Entity entity, Edge edge) {
+ this.entity = entity;
+ this.edge = edge;
+ }
+
+ public boolean isCollision() {
+ return entity != null && edge != null;
+ }
+
+ public Entity getEntity() {
+ return entity;
+ }
+
+ public Edge getEdge() {
+ return edge;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/ObstacleCollisionData.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/ObstacleCollisionData.java
new file mode 100644
index 0000000..ff697a2
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/ObstacleCollisionData.java
@@ -0,0 +1,23 @@
+package de.frajul.endlessroll.entities.collision.collisionData;
+
+import java.util.List;
+
+/**
+ * Created by Julian on 04.01.2016.
+ */
+public class ObstacleCollisionData {
+
+ private List collisions;
+
+ public ObstacleCollisionData(List collisions) {
+ this.collisions = collisions;
+ }
+
+ public boolean isCollision() {
+ return collisions.size() > 0;
+ }
+
+ public List getCollisions() {
+ return collisions;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/PlayerCollisionData.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/PlayerCollisionData.java
new file mode 100644
index 0000000..da100c1
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/PlayerCollisionData.java
@@ -0,0 +1,71 @@
+package de.frajul.endlessroll.entities.collision.collisionData;
+
+/**
+ * Created by Julian on 05.12.2015.
+ */
+public class PlayerCollisionData {
+
+ private EntityCollisionData terrainCollision;
+ private EntityCollisionData ceilingCollision;
+ private ObstacleCollisionData obstacleCollision;
+ private ToolCollisionData toolCollision;
+ private EntityCollisionData starCollision;
+ private EntityCollisionData energyCollision;
+
+ public PlayerCollisionData(EntityCollisionData terrainCollision, EntityCollisionData ceilingCollision, ObstacleCollisionData obstacleCollision, ToolCollisionData toolCollision, EntityCollisionData starCollision, EntityCollisionData energyCollision) {
+ this.terrainCollision = terrainCollision;
+ this.ceilingCollision = ceilingCollision;
+ this.obstacleCollision = obstacleCollision;
+ this.toolCollision = toolCollision;
+ this.starCollision = starCollision;
+ this.energyCollision = energyCollision;
+ }
+
+ public boolean isTerrainCollision() {
+ return terrainCollision.isCollision();
+ }
+
+ public EntityCollisionData getTerrainCollisionData() {
+ return terrainCollision;
+ }
+
+ public boolean isCeilingCollision() {
+ return ceilingCollision.isCollision();
+ }
+
+ public EntityCollisionData getCeilingCollisionData() {
+ return ceilingCollision;
+ }
+
+ public boolean isObstacleCollision() {
+ return obstacleCollision.isCollision();
+ }
+
+ public ObstacleCollisionData getObstacleCollisionData() {
+ return obstacleCollision;
+ }
+
+ public boolean isToolCollision() {
+ return toolCollision.isCollision();
+ }
+
+ public ToolCollisionData getToolCollisionData() {
+ return toolCollision;
+ }
+
+ public boolean isStarCollision() {
+ return starCollision.isCollision();
+ }
+
+ public EntityCollisionData getStarCollisionData() {
+ return starCollision;
+ }
+
+ public boolean isEnergyCollision() {
+ return energyCollision.isCollision();
+ }
+
+ public EntityCollisionData getEnergyCollisionData() {
+ return energyCollision;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/ToolCollisionData.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/ToolCollisionData.java
new file mode 100644
index 0000000..1f02efa
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/collisionData/ToolCollisionData.java
@@ -0,0 +1,25 @@
+package de.frajul.endlessroll.entities.collision.collisionData;
+
+import de.frajul.endlessroll.entities.tools.Tool;
+
+import java.util.List;
+
+/**
+ * Created by Julian on 21.02.2016.
+ */
+public class ToolCollisionData {
+
+ private List tools;
+
+ public ToolCollisionData(List tools) {
+ this.tools = tools;
+ }
+
+ public boolean isCollision() {
+ return tools.size() > 0;
+ }
+
+ public List getTools() {
+ return tools;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Circle.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Circle.java
new file mode 100644
index 0000000..526d9f3
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Circle.java
@@ -0,0 +1,51 @@
+package de.frajul.endlessroll.entities.collision.geometry;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collision.CollisionDetector;
+
+/**
+ * Created by Julian on 01.12.2015.
+ */
+public class Circle implements Geometry {
+
+ private Vector center;
+ private float radius;
+
+ public Circle(Player ball) {
+ this(new Vector(ball.getPosition()), ball.getWidth() / 2);
+ }
+
+ public Circle(Vector center, float radius) {
+ this.center = center;
+ this.radius = radius;
+ }
+
+ public Vector getCenter() {
+ return center;
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public void setRadius(float radius) {
+ this.radius = radius;
+ }
+
+ @Override
+ public boolean isCollisionWithCircle(Circle circle, CollisionDetector collisionDetector){
+ return collisionDetector.isCircleCircleCollision(this, circle);
+ }
+
+ @Override
+ public boolean isCollisionWithQuad(Quad quad, CollisionDetector collisionDetector){
+ return collisionDetector.isCircleQuadCollision(this, quad);
+ }
+
+ @Override
+ public boolean isCollisionWithTriangle(Triangle triangle, CollisionDetector collisionDetector){
+ return collisionDetector.isCircleTriangleCollision(this, triangle);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Geometry.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Geometry.java
new file mode 100644
index 0000000..f539fd2
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Geometry.java
@@ -0,0 +1,17 @@
+package de.frajul.endlessroll.entities.collision.geometry;
+
+import de.frajul.endlessroll.entities.collision.CollisionDetector;
+
+/**
+ * Created by Julian on 28.02.2016.
+ */
+public interface Geometry {
+
+ boolean isCollisionWithCircle(Circle circle, CollisionDetector collisionDetector);
+
+ boolean isCollisionWithQuad(Quad quad, CollisionDetector collisionDetector);
+
+ boolean isCollisionWithTriangle(Triangle triangle, CollisionDetector collisionDetector);
+
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Quad.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Quad.java
new file mode 100644
index 0000000..0791fb8
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Quad.java
@@ -0,0 +1,75 @@
+package de.frajul.endlessroll.entities.collision.geometry;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.collision.CollisionDetector;
+
+/**
+ * Created by Julian on 01.12.2015.
+ */
+public class Quad implements Geometry {
+
+ protected Vector position;
+ protected float width, height;
+
+ public Quad(Vector position, float width, float height) {
+ this.position = position;
+ this.width = width;
+ this.height = height;
+ }
+
+ public float getRightEdge() {
+ return position.x + width / 2;
+ }
+
+ public float getLeftEdge() {
+ return position.x - width / 2;
+ }
+
+ public float getTopEdge() {
+ return position.y + height / 2;
+ }
+
+ public float getBottomEdge() {
+ return position.y - height / 2;
+ }
+
+ public Vector getPosition() {
+ return position;
+ }
+
+ public void setPosition(Vector position) {
+ this.position = position;
+ }
+
+ public float getWidth() {
+ return width;
+ }
+
+ public void setWidth(float width) {
+ this.width = width;
+ }
+
+ public float getHeight() {
+ return height;
+ }
+
+ public void setHeight(float height) {
+ this.height = height;
+ }
+
+ @Override
+ public boolean isCollisionWithCircle(Circle circle, CollisionDetector collisionDetector) {
+ return collisionDetector.isCircleQuadCollision(circle, this);
+ }
+
+ @Override
+ public boolean isCollisionWithQuad(Quad quad, CollisionDetector collisionDetector) {
+ return collisionDetector.isQuadQuadCollision(this, quad);
+ }
+
+ @Override
+ public boolean isCollisionWithTriangle(Triangle triangle, CollisionDetector collisionDetector) {
+ return collisionDetector.isQuadTriangleCollision(this, triangle);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Triangle.java b/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Triangle.java
new file mode 100644
index 0000000..ea6cdc1
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/collision/geometry/Triangle.java
@@ -0,0 +1,61 @@
+package de.frajul.endlessroll.entities.collision.geometry;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.collision.CollisionDetector;
+import de.frajul.endlessroll.entities.tools.Ramp;
+
+/**
+ * Created by Julian on 01.12.2015.
+ */
+public class Triangle implements Geometry {
+
+ //bottom right
+ private Vector vertex1;
+ //top right
+ private Vector vertex2;
+ //bottom left
+ private Vector vertex3;
+
+ public Triangle(Ramp ramp) {
+ Vector vertex1 = new Vector(ramp.getRightEdge(), ramp.getBottomEdge());
+ Vector vertex2 = new Vector(ramp.getRightEdge(), ramp.getTopEdge());
+ Vector vertex3 = new Vector(ramp.getLeftEdge(), ramp.getBottomEdge());
+ this.vertex1 = vertex1;
+ this.vertex2 = vertex2;
+ this.vertex3 = vertex3;
+ }
+
+ public Triangle(Vector vertex1, Vector vertex2, Vector vertex3) {
+ this.vertex1 = vertex1;
+ this.vertex2 = vertex2;
+ this.vertex3 = vertex3;
+ }
+
+ public Vector getVertex1() {
+ return vertex1;
+ }
+
+ public Vector getVertex2() {
+ return vertex2;
+ }
+
+ public Vector getVertex3() {
+ return vertex3;
+ }
+
+ @Override
+ public boolean isCollisionWithCircle(Circle circle, CollisionDetector collisionDetector) {
+ return collisionDetector.isCircleTriangleCollision(circle, this);
+ }
+
+ @Override
+ public boolean isCollisionWithQuad(Quad quad, CollisionDetector collisionDetector) {
+ return collisionDetector.isQuadTriangleCollision(quad, this);
+ }
+
+ @Override
+ public boolean isCollisionWithTriangle(Triangle triangle, CollisionDetector collisionDetector) {
+ return collisionDetector.isTriangleTriangleCollision(this, triangle);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/Particle.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/Particle.java
new file mode 100644
index 0000000..63c2175
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/Particle.java
@@ -0,0 +1,111 @@
+package de.frajul.endlessroll.entities.particles;
+
+import java.util.Random;
+
+import de.frajul.endlessroll.data.Color;
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Range;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.TintTimeline;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Particle extends Entity {
+
+ private Color color;
+ private Random random;
+ private boolean active;
+ private float maxLife;
+ private float passedLifetime;
+
+ private Timeline scaleTimeline;
+ private Range scale;
+ private Timeline velocityTimeline;
+ private Range velocity;
+ private Timeline angleTimeline;
+ private Range angle;
+ private Timeline rotationTimeline;
+ private Range rotation;
+ private Timeline transparencyTimeline;
+ private TintTimeline tintTimeline;
+ private Timeline windTimeline;
+ private Range wind;
+ private Timeline gravityTimeline;
+ private Range gravity;
+
+ public Particle(Random random) {
+ super(null, new Vector(), 1, 1);
+ this.random = random;
+ }
+
+ public void activate(Vector position, float liveValue, ParticleData particleData) {
+ active = true;
+ passedLifetime = 0;
+ super.setPosition(position);
+ maxLife = particleData.getLife().createValue(random, liveValue);
+ scaleTimeline = particleData.getScaleTR().getTimeline();
+ scale = particleData.getScaleTR().getRange().createNormalizedInstance(random);
+ velocityTimeline = particleData.getVelocityTR().getTimeline();
+ velocity = particleData.getVelocityTR().getRange().createNormalizedInstance(random);
+ angleTimeline = particleData.getAngleTR().getTimeline();
+ angle = particleData.getAngleTR().getRange().createNormalizedInstance(random);
+ rotationTimeline = particleData.getRotationTR().getTimeline();
+ rotation = particleData.getRotationTR().getRange().createNormalizedInstance(random);
+ transparencyTimeline = particleData.getTransparencyT();
+ tintTimeline = particleData.getTint();
+ windTimeline = particleData.getWindTR().getTimeline();
+ wind = particleData.getWindTR().getRange().createNormalizedInstance(random);
+ gravityTimeline = particleData.getGravityTR().getTimeline();
+ gravity = particleData.getGravityTR().getRange().createNormalizedInstance(random);
+ }
+
+ public void update(Timer timer) {
+ if (active) {
+ passedLifetime += timer.getFrameTimeSeconds();
+ if (passedLifetime >= maxLife)
+ active = false;
+ float lifetimePercent = passedLifetime / maxLife;
+ setScale(scale.createValue(random, scaleTimeline.getValueAtTime(lifetimePercent)));
+ setMovement(
+ velocity.createValue(random, velocityTimeline.getValueAtTime(lifetimePercent)),
+ angle.createValue(random, angleTimeline.getValueAtTime(lifetimePercent)),
+ wind.createValue(random, windTimeline.getValueAtTime(lifetimePercent)),
+ gravity.createValue(random, gravityTimeline.getValueAtTime(lifetimePercent)));
+ super.setRotation(-rotation
+ .createValue(random, rotationTimeline.getValueAtTime(lifetimePercent)));
+ super.setAlpha(transparencyTimeline.getValueAtTime(lifetimePercent));
+ setColor(tintTimeline.getValueAtTime(lifetimePercent));
+ }
+ }
+
+ private void setScale(float scale) {
+ super.setWidth(scale * ParticleSystem.TRANSFER_VALUE);
+ super.setHeight(scale * ParticleSystem.TRANSFER_VALUE);
+ }
+
+ private void setMovement(float velocity, float angle, float windStrength, float gravityStrength) {
+ float radians = (float) Math.toRadians(angle);
+ Vector normalMovement = new Vector();
+ normalMovement.setX((float) Math.cos(radians));
+ normalMovement.setY((float) Math.sin(radians));
+ normalMovement.mul(velocity * ParticleSystem.TRANSFER_VALUE);
+ normalMovement.translate(windStrength * ParticleSystem.TRANSFER_VALUE,
+ gravityStrength * ParticleSystem.TRANSFER_VALUE);
+ super.setMovement(normalMovement);
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleData.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleData.java
new file mode 100644
index 0000000..dda6491
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleData.java
@@ -0,0 +1,70 @@
+package de.frajul.endlessroll.entities.particles;
+
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Range;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.TimelineRange;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.TintTimeline;
+
+/**
+ * Created by Julian on 03.08.2016.
+ */
+public class ParticleData {
+
+ private Range life;
+ private TimelineRange scaleTR;
+ private TimelineRange velocityTR;
+ private TimelineRange angleTR;
+ private TimelineRange rotationTR;
+ private Timeline transparencyT;
+ private TintTimeline tint;
+ private TimelineRange windTR;
+ private TimelineRange gravityTR;
+
+ public ParticleData(Range life, TimelineRange scaleTR, TimelineRange velocityTR, TimelineRange angleTR, TimelineRange rotationTR, Timeline transparencyT, TintTimeline tint, TimelineRange windTR, TimelineRange gravityTR) {
+ this.life = life;
+ this.scaleTR = scaleTR;
+ this.velocityTR = velocityTR;
+ this.angleTR = angleTR;
+ this.rotationTR = rotationTR;
+ this.transparencyT = transparencyT;
+ this.tint = tint;
+ this.windTR = windTR;
+ this.gravityTR = gravityTR;
+ }
+
+ public Range getLife() {
+ return life;
+ }
+
+ public TimelineRange getScaleTR() {
+ return scaleTR;
+ }
+
+ public TimelineRange getVelocityTR() {
+ return velocityTR;
+ }
+
+ public TimelineRange getAngleTR() {
+ return angleTR;
+ }
+
+ public TimelineRange getRotationTR() {
+ return rotationTR;
+ }
+
+ public Timeline getTransparencyT() {
+ return transparencyT;
+ }
+
+ public TintTimeline getTint() {
+ return tint;
+ }
+
+ public TimelineRange getWindTR() {
+ return windTR;
+ }
+
+ public TimelineRange getGravityTR() {
+ return gravityTR;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleEffect.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleEffect.java
new file mode 100644
index 0000000..7e1f616
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleEffect.java
@@ -0,0 +1,229 @@
+package de.frajul.endlessroll.entities.particles;
+
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Options;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Range;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.SpawnShape;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.TimelineRange;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.TintTimeline;
+import de.frajul.endlessroll.entities.textures.Texture;
+import de.frajul.endlessroll.main.game.Timer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Created by Julian on 05.08.2016.
+ */
+public class ParticleEffect {
+
+ private TimelineRange life;
+ //Particle Timeline
+ private TimelineRange scale;
+ private TimelineRange velocity;
+ private TimelineRange angle;
+ private TimelineRange rotation;
+ private Timeline transparency;
+ private TintTimeline tint;
+ private TimelineRange wind;
+ private TimelineRange gravity;
+
+ //Source Timeline
+ private Range delay;
+ private Range duration;
+ private TimelineRange emission;
+ private Range xOffset;
+ private Range yOffset;
+ private SpawnShape.Shape spawnShape;
+ private TimelineRange spawnWidth;
+ private TimelineRange spawnHeight;
+
+ private Options options;
+ private Texture texture;
+ private String textureName;
+
+ private Random random;
+ private List sources = Collections.synchronizedList(new ArrayList());
+
+ public ParticleEffect() {
+ this.random = new Random();
+ }
+
+ public void update(Timer timer) {
+ synchronized (sources) {
+ for (ParticleSource source : sources)
+ source.update(timer);
+ }
+ }
+
+ public ParticleData createParticleData() {
+ return new ParticleData(life.getRange(), scale, velocity, angle, rotation, transparency, tint, wind , gravity);
+ }
+
+ public Random getRandom() {
+ return random;
+ }
+
+ public void addSource(ParticleSource source) {
+ sources.add(source);
+ }
+
+ public void setDelay(Range delay) {
+ this.delay = delay;
+ }
+
+ public void setDuration(Range duration) {
+ this.duration = duration;
+ }
+
+ public void setEmission(TimelineRange emission) {
+ this.emission = emission;
+ }
+
+ public void setLife(TimelineRange life) {
+ this.life = life;
+ }
+
+ public void setxOffset(Range xOffset) {
+ this.xOffset = xOffset;
+ }
+
+ public void setyOffset(Range yOffset) {
+ this.yOffset = yOffset;
+ }
+
+ public void setSpawnShape(SpawnShape.Shape spawnShape) {
+ this.spawnShape = spawnShape;
+ }
+
+ public void setSpawnWidth(TimelineRange spawnWidth) {
+ this.spawnWidth = spawnWidth;
+ }
+
+ public void setSpawnHeight(TimelineRange spawnHeight) {
+ this.spawnHeight = spawnHeight;
+ }
+
+ public void setScale(TimelineRange scale) {
+ this.scale = scale;
+ }
+
+ public void setVelocity(TimelineRange velocity) {
+ this.velocity = velocity;
+ }
+
+ public void setAngle(TimelineRange angle) {
+ this.angle = angle;
+ }
+
+ public void setRotation(TimelineRange rotation) {
+ this.rotation = rotation;
+ }
+
+ public void setWind(TimelineRange wind) {
+ this.wind = wind;
+ }
+
+ public void setGravity(TimelineRange gravity) {
+ this.gravity = gravity;
+ }
+
+ public void setTint(TintTimeline tint) {
+ this.tint = tint;
+ }
+
+ public void setTransparency(Timeline transparency) {
+ this.transparency = transparency;
+ }
+
+ public Timeline getTransparency() {
+ return transparency;
+ }
+
+ public TintTimeline getTint() {
+ return tint;
+ }
+
+ public void setOptions(Options options) {
+ this.options = options;
+ }
+
+ public void setTexture(Texture texture) {
+ this.texture = texture;
+ }
+
+ public void setTextureName(String textureName) {
+ this.textureName = textureName;
+ }
+
+ public String getTextureName() {
+ return textureName;
+ }
+
+ public Texture getTexture() {
+ return texture;
+ }
+
+ public Range getDelay() {
+ return delay;
+ }
+
+ public Range getDuration() {
+ return duration;
+ }
+
+ public TimelineRange getEmission() {
+ return emission;
+ }
+
+ public Options getOptions() {
+ return options;
+ }
+
+ public TimelineRange getWind() {
+ return wind;
+ }
+
+ public TimelineRange getGravity() {
+ return gravity;
+ }
+
+ public Range getxOffset() {
+ return xOffset;
+ }
+
+ public Range getyOffset() {
+ return yOffset;
+ }
+
+ public synchronized List getSources() {
+ return sources;
+ }
+
+ public TimelineRange getLife() {
+ return life;
+ }
+
+ public SpawnShape.Shape getSpawnShape() {
+ return spawnShape;
+ }
+
+ public TimelineRange getSpawnHeight() {
+ return spawnHeight;
+ }
+
+ public void deleteSources() {
+ synchronized (sources) {
+ for (ParticleSource source : sources)
+ source.kill();
+ sources.clear();
+ }
+ }
+
+ public TimelineRange getSpawnWidth() {
+ return spawnWidth;
+ }
+}
+
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleReader.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleReader.java
new file mode 100644
index 0000000..381ce89
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleReader.java
@@ -0,0 +1,163 @@
+package de.frajul.endlessroll.entities.particles;
+
+import android.content.Context;
+
+import de.frajul.endlessroll.entities.particles.attributes.Attribute;
+import de.frajul.endlessroll.entities.particles.attributes.AttributeValueReader;
+import de.frajul.endlessroll.entities.particles.attributes.ParticleAttributeType;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.ImagePath;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Options;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.ParticleAttributeValueType;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Range;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.SpawnShape;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.TimelineRange;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.TintTimeline;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class ParticleReader {
+
+ private Context context;
+ private AttributeValueReader attributeValueReader;
+
+ private List attributes;
+ private Attribute currentAttribute;
+
+ public ParticleReader(Context context) {
+ this.context = context;
+ attributeValueReader = new AttributeValueReader();
+ }
+
+ /**
+ * reads ParticleEffect from *.pe files
+ * !Ignores COUNT, LIFE_OFFSET!
+ */
+ public ParticleEffect read(String filename) throws Exception {
+ try {
+ attributes = new ArrayList<>();
+ currentAttribute = null;
+ InputStream is = context.getAssets().open(filename);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ handleLine(line);
+ }
+ reader.close();
+ return createParticleEffect();
+ } catch (Exception e) {
+ throw new Exception("Could not read particleFile: ", e);
+ }
+ }
+
+ private void handleLine(String line) throws Exception {
+ if (line.startsWith("- ")) {
+ Attribute attrib = ParticleAttributeType.getByInFileTitle(line).createInstance();
+ attributes.add(attrib);
+ currentAttribute = attrib;
+ } else if (currentAttribute != null)
+ attributeValueReader.addValueForAttribute(currentAttribute, line);
+ }
+
+ private ParticleEffect createParticleEffect() throws Exception {
+ ParticleEffect effect = new ParticleEffect();
+ Timeline timeline = null;
+ Range range = null;
+ for (Attribute attribute : attributes) {
+ switch (attribute.getType()) {
+ case DELAY:
+ effect.setDelay((Range) attribute.get(ParticleAttributeValueType.RANGE));
+ break;
+ case DURATION:
+ effect.setDuration((Range) attribute.get(ParticleAttributeValueType.RANGE));
+ break;
+ case COUNT:
+ break;
+ case EMISSION:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setEmission(new TimelineRange(timeline, range));
+ break;
+ case LIFE:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setLife(new TimelineRange(timeline, range));
+ break;
+ case LIFE_OFFSET:
+ break;
+ case X_OFFSET:
+ effect.setxOffset((Range) attribute.get(ParticleAttributeValueType.RANGE));
+ break;
+ case Y_OFFSET:
+ effect.setyOffset((Range) attribute.get(ParticleAttributeValueType.RANGE));
+ break;
+ case SPAWN_SHAPE:
+ effect.setSpawnShape(((SpawnShape) attribute.get(ParticleAttributeValueType.SPAWN_SHAPE)).getShape());
+ break;
+ case SPAWN_WIDTH:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setSpawnWidth(new TimelineRange(timeline, range));
+ break;
+ case SPAWN_HEIGHT:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setSpawnHeight(new TimelineRange(timeline, range));
+ break;
+ case SCALE:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setScale(new TimelineRange(timeline, range));
+ break;
+ case VELOCITY:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setVelocity(new TimelineRange(timeline, range));
+ break;
+ case ANGLE:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setAngle(new TimelineRange(timeline, range));
+ break;
+ case ROTATION:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setRotation(new TimelineRange(timeline, range));
+ break;
+ case WIND:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setWind(new TimelineRange(timeline, range));
+ break;
+ case GRAVITY:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setGravity(new TimelineRange(timeline, range));
+ break;
+ case TINT:
+ effect.setTint((TintTimeline) attribute.get(ParticleAttributeValueType.TINT_TIMELINE));
+ break;
+ case TRANSPARENCY:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ effect.setTransparency(timeline);
+ break;
+ case OPTIONS:
+ effect.setOptions((Options) attribute.get(ParticleAttributeValueType.OPTIONS));
+ break;
+ case IMAGE_PATH:
+ String path = ((ImagePath) attribute.get(ParticleAttributeValueType.IMAGE_PATH)).getImagePath();
+ effect.setTextureName(path);
+ break;
+ }
+ }
+ return effect;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleSource.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleSource.java
new file mode 100644
index 0000000..a21a254
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleSource.java
@@ -0,0 +1,176 @@
+package de.frajul.endlessroll.entities.particles;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.SpawnShape;
+import de.frajul.endlessroll.main.GameLog;
+import de.frajul.endlessroll.main.game.Timer;
+import de.frajul.endlessroll.rendering.Lock;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class ParticleSource {
+
+ private Vector position;
+ private ParticleEffect effect;
+ private Random random;
+
+ private boolean delayManuallySet = false;
+
+ private boolean alife;
+ private float currentDelay;
+ private float lifePercent;
+ private float maxTime = -1;
+ private float passedTime;
+ private float emittPause;
+ private float passedEmittPause;
+ private float passedSecond;
+
+ private Vector spawnSize = null;
+ private ParticleData particleData;
+ private List activeParticles = new ArrayList<>();
+ private List inactiveParticles = new ArrayList<>();
+ private Lock activeParticleLock = new Lock();
+
+ public ParticleSource(Vector position, ParticleEffect effect) {
+ this.position = position;
+ this.effect = effect;
+ random = effect.getRandom();
+ effect.addSource(this);
+ }
+
+ public void start() {
+ alife = true;
+ if (!delayManuallySet)
+ currentDelay = effect.getDelay().createValue(random, 0);
+ maxTime = effect.getDuration().createValue(random, 0) + currentDelay;
+ passedTime = 0;
+ emittPause = calcEmittPause();
+ passedEmittPause = 0;
+ passedSecond = 0;
+ lifePercent = 0;
+ }
+
+ public void update(Timer timer) {
+ if (alife) {
+ passedTime += timer.getFrameTimeSeconds();
+ lifePercent = passedTime / maxTime;
+
+ if (passedTime >= currentDelay) {
+ passedEmittPause += timer.getFrameTimeSeconds();
+ while (passedEmittPause >= emittPause) {
+ passedEmittPause -= emittPause;
+ emitt();
+ }
+
+ passedSecond += timer.getFrameTimeSeconds();
+ if (passedSecond >= 1000) {
+ passedSecond -= 1000;
+ calcEmittPause();
+ }
+ }
+ if (passedTime >= maxTime)
+ die();
+ }
+ updateParticles(timer);
+ }
+
+ private void updateParticles(Timer timer) {
+ activeParticleLock.lock();
+ Iterator iter = activeParticles.iterator();
+ while (iter.hasNext()) {
+ Particle particle = iter.next();
+ particle.update(timer);
+ if (!particle.isActive()) {
+ inactiveParticles.add(particle);
+ iter.remove();
+ } else {
+ particle.move(
+ new Vector(particle.getMovement()).mul(timer.getFrameTimeSeconds() / 1000));
+ }
+ }
+ activeParticleLock.unlock();
+ }
+
+ private void die() {
+ alife = false;
+ if (effect.getOptions().isContinuous())
+ start();
+ }
+
+ public void kill() {
+ alife = false;
+ }
+
+ public void emitt() {
+ if (particleData == null)
+ particleData = effect.createParticleData();
+ float xOff = effect.getxOffset().createValue(random, 0) * ParticleSystem.TRANSFER_VALUE;
+ float yOff = effect.getyOffset().createValue(random, 0) * ParticleSystem.TRANSFER_VALUE;
+ if (effect.getSpawnShape() == SpawnShape.Shape.SQUARE || effect.getSpawnShape() == SpawnShape.Shape.LINE) {
+ float width, height;
+ if (spawnSize == null) {
+ width = effect.getSpawnWidth().getRange().createValue(random,
+ effect.getSpawnWidth().getTimeline().getValueAtTime(lifePercent));
+ height = effect.getSpawnHeight().getRange().createValue(random,
+ effect.getSpawnHeight().getTimeline().getValueAtTime(lifePercent));
+ } else {
+ width = spawnSize.getX() / ParticleSystem.TRANSFER_VALUE;
+ height = spawnSize.getY() / ParticleSystem.TRANSFER_VALUE;
+ }
+ xOff += (random.nextFloat() * width - width * 0.5f) * ParticleSystem.TRANSFER_VALUE;
+ yOff += (random.nextFloat() * height - height * 0.5f) * ParticleSystem.TRANSFER_VALUE;
+ }
+ setUpParticle(new Vector(position).translate(xOff, yOff));
+ }
+
+ private Particle setUpParticle(Vector position) {
+ Particle particle;
+ if (inactiveParticles.size() > 0)
+ particle = inactiveParticles.remove(0);
+ else
+ particle = new Particle(random);
+
+ particle.activate(position, effect.getLife().getTimeline().getValueAtTime(lifePercent),
+ particleData);
+ activeParticleLock.lock();
+ activeParticles.add(particle);
+ activeParticleLock.unlock();
+ return particle;
+ }
+
+ private float calcEmittPause() {
+ float emittedPerSecond = effect.getEmission().getRange().createValue(random,
+ effect.getEmission().getTimeline().getValueAtTime(passedTime / maxTime));
+ return 1000 / emittedPerSecond;
+ }
+
+ public Lock getActiveParticleLock() {
+ return activeParticleLock;
+ }
+
+ public List getActiveParticles() {
+ return activeParticles;
+ }
+
+ public void setSpawnSize(Vector spawnSize) {
+ this.spawnSize = spawnSize;
+ }
+
+ public void setPosition(Vector position) {
+ this.position = position;
+ }
+
+ public void setDelayMS(float delay) {
+ currentDelay = delay;
+ delayManuallySet = true;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleSystem.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleSystem.java
new file mode 100644
index 0000000..a00df18
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/ParticleSystem.java
@@ -0,0 +1,69 @@
+package de.frajul.endlessroll.entities.particles;
+
+import android.content.Context;
+
+import de.frajul.endlessroll.entities.textures.TextureLoader;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class ParticleSystem {
+
+ public static final float TRANSFER_VALUE = 0.002f;
+ public final ParticleEffect stasis;
+ public final ParticleEffect testFire;
+ public final ParticleEffect colorChange;
+ public final ParticleEffect explosion;
+ public final ParticleEffect magnet;
+ public final ParticleEffect starCollect;
+ public final ParticleEffect energyCollect;
+ public final ParticleEffect firework;
+ public final ParticleEffect superPower;
+ private ParticleEffect[] effects;
+
+ private TextureLoader textureLoader;
+
+ public ParticleSystem(Context context) throws Exception {
+ this.textureLoader = new TextureLoader(context);
+ ParticleReader reader = new ParticleReader(context);
+ stasis = reader.read("particleEffects/stasis.pe");
+ testFire = reader.read("particleEffects/test_fire.pe");
+ colorChange = reader.read("particleEffects/colorChange.pe");
+ explosion = reader.read("particleEffects/explosion.pe");
+ magnet = reader.read("particleEffects/magnet.pe");
+ starCollect = reader.read("particleEffects/collectStar.pe");
+ energyCollect = reader.read("particleEffects/collectEnergy.pe");
+ firework = reader.read("particleEffects/firework.pe");
+ superPower = reader.read("particleEffects/superPower.pe");
+
+ effects = new ParticleEffect[]{stasis, testFire,colorChange, explosion, magnet, starCollect, energyCollect, firework, superPower};
+ }
+
+ public void update(Timer timer) {
+ synchronized (effects) {
+ for (ParticleEffect effect : effects)
+ effect.update(timer);
+ }
+ }
+
+ public void loadTextures() throws Exception {
+ synchronized (effects) {
+ for (ParticleEffect effect : effects)
+ effect.setTexture(
+ textureLoader.loadTexture("particleEffects/" + effect.getTextureName()));
+ }
+ }
+
+ public void deleteAllSources() {
+ synchronized (effects) {
+ for (ParticleEffect effect : effects) {
+ effect.deleteSources();
+ }
+ }
+ }
+
+ public synchronized ParticleEffect[] getEffects() {
+ return effects;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/Attribute.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/Attribute.java
new file mode 100644
index 0000000..072a599
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/Attribute.java
@@ -0,0 +1,33 @@
+package de.frajul.endlessroll.entities.particles.attributes;
+
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.ParticleAttributeValue;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.ParticleAttributeValueType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Attribute {
+
+ private ParticleAttributeType type;
+ private List values = new ArrayList<>();
+
+ public Attribute(ParticleAttributeType type, ParticleAttributeValueType... valueTypes) {
+ this.type = type;
+ for (ParticleAttributeValueType valueType : valueTypes)
+ values.add(valueType.createInstance());
+ }
+
+ public ParticleAttributeValue get(ParticleAttributeValueType valueType) throws Exception {
+ for (ParticleAttributeValue v : values)
+ if (v.getType() == valueType)
+ return v;
+ throw new Exception("ParticleAttributeValue with type: " + valueType + " does not exist in Attribute " + type);
+ }
+
+ public ParticleAttributeType getType() {
+ return type;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/AttributeValueReader.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/AttributeValueReader.java
new file mode 100644
index 0000000..c9f774a
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/AttributeValueReader.java
@@ -0,0 +1,77 @@
+package de.frajul.endlessroll.entities.particles.attributes;
+
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.ImagePath;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Options;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.ParticleAttributeValueType;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Range;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.SpawnShape;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.TintTimeline;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class AttributeValueReader {
+
+ public void addValueForAttribute(Attribute attribute, String line) throws Exception {
+ //RANGE
+ if (line.startsWith("lowMin:") || line.startsWith("min:"))
+ ((Range) attribute.get(ParticleAttributeValueType.RANGE)).setLowMin(parseFloat(line));
+ else if (line.startsWith("lowMax:") || line.startsWith("max:"))
+ ((Range) attribute.get(ParticleAttributeValueType.RANGE)).setLowMax(parseFloat(line));
+ else if (line.startsWith("highMin:"))
+ ((Range) attribute.get(ParticleAttributeValueType.RANGE)).setHighMin(parseFloat(line));
+ else if (line.startsWith("highMax:"))
+ ((Range) attribute.get(ParticleAttributeValueType.RANGE)).setHighMax(parseFloat(line));
+ //TIMELINE
+ else if (!line.startsWith("scalingCount") && line.startsWith("scaling"))
+ ((Timeline) attribute.get(ParticleAttributeValueType.TIMELINE)).setValueOfPoint(parseTimeLineIndex("scaling", line), parseFloat(line));
+ else if (!line.startsWith("timelineCount") && line.startsWith("timeline") && attribute.getType() != ParticleAttributeType.TINT)
+ ((Timeline) attribute.get(ParticleAttributeValueType.TIMELINE)).setTimeOfPoint(parseTimeLineIndex("timeline", line), parseFloat(line));
+ //TINT_TIMELINE
+ else if (!line.startsWith("colorsCount") && line.startsWith("colors")) {
+ int index = parseTimeLineIndex("colors", line);
+ ((TintTimeline) attribute.get(ParticleAttributeValueType.TINT_TIMELINE)).setValueOfPoint((int) (index / 3f), index % 3, parseFloat(line));
+ } else if (!line.startsWith("timelineCount") && line.startsWith("timeline") && attribute.getType() == ParticleAttributeType.TINT)
+ ((TintTimeline) attribute.get(ParticleAttributeValueType.TINT_TIMELINE)).setTimeOfPoint(parseTimeLineIndex("timeline", line), parseFloat(line));
+ //SPAWN_SHAPE
+ else if (line.startsWith("shape:"))
+ ((SpawnShape) attribute.get(ParticleAttributeValueType.SPAWN_SHAPE)).setShape(SpawnShape.Shape.byName(parseString(line)));
+ //OPTIONS
+ else if (line.startsWith("attached:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setAttached(parseBoolean(line));
+ else if (line.startsWith("continuous:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setContinuous(parseBoolean(line));
+ else if (line.startsWith("aligned:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setAligned(parseBoolean(line));
+ else if (line.startsWith("additive:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setAdditive(parseBoolean(line));
+ else if (line.startsWith("behind:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setBehind(parseBoolean(line));
+ else if (line.startsWith("premultipliedAlpha:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setPremultipliedAlpha(parseBoolean(line));
+ //IMAGE PATH
+ else if (attribute.getType() == ParticleAttributeType.IMAGE_PATH)
+ ((ImagePath) attribute.get(ParticleAttributeValueType.IMAGE_PATH)).setImagePath(line);
+ }
+
+ private int parseTimeLineIndex(String start, String line) throws Exception {
+ String asString = line.split(start)[1].split(":")[0];
+ return Integer.parseInt(asString);
+ }
+
+ private float parseFloat(String line) throws Exception {
+ String asString = line.split(" ")[1];
+ return Float.parseFloat(asString);
+ }
+
+ private String parseString(String line) throws Exception {
+ return line.split(" ")[1];
+ }
+
+ private boolean parseBoolean(String line) throws Exception {
+ String asString = line.split(" ")[1];
+ return Boolean.parseBoolean(asString);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/ParticleAttributeType.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/ParticleAttributeType.java
new file mode 100644
index 0000000..ab6bf97
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/ParticleAttributeType.java
@@ -0,0 +1,55 @@
+package de.frajul.endlessroll.entities.particles.attributes;
+
+import de.frajul.endlessroll.entities.particles.attributes.attributeValues.ParticleAttributeValueType;
+
+public enum ParticleAttributeType {
+ NONE(""),
+ DELAY("Delay", ParticleAttributeValueType.RANGE),
+ DURATION("Duration", ParticleAttributeValueType.RANGE),
+ COUNT("Count", ParticleAttributeValueType.RANGE),
+ EMISSION("Emission", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ LIFE("Life", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ LIFE_OFFSET("Life Offset", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ X_OFFSET("X Offset", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ Y_OFFSET("Y Offset", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ SPAWN_SHAPE("Spawn Shape", ParticleAttributeValueType.SPAWN_SHAPE),
+ SPAWN_WIDTH("Spawn Width", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ SPAWN_HEIGHT("Spawn Height", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ SCALE("Scale", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ VELOCITY("Velocity", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ ANGLE("Angle", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ ROTATION("Rotation", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ WIND("Wind", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ GRAVITY("Gravity", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ TINT("Tint", ParticleAttributeValueType.TINT_TIMELINE),
+ TRANSPARENCY("Transparency", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ OPTIONS("Options", ParticleAttributeValueType.OPTIONS),
+ IMAGE_PATH("Image Path", ParticleAttributeValueType.IMAGE_PATH);
+
+ private String name;
+ private ParticleAttributeValueType[] valueTypes;
+
+ ParticleAttributeType(String name, ParticleAttributeValueType... valueTypes) {
+ this.name = name;
+ this.valueTypes = valueTypes;
+ }
+
+ private String getInFileTitle() {
+ return "- " + name + " -";
+ }
+
+ public static ParticleAttributeType getByInFileTitle(String title) throws Exception {
+ for (ParticleAttributeType setting : values()) {
+ if (setting != NONE && setting.getInFileTitle().equals(title))
+ return setting;
+ }
+ throw new Exception("Could not find ParticleAttributeType by title: " + title);
+ }
+
+ public Attribute createInstance() throws Exception {
+ if (this == NONE)
+ throw new Exception("Cannot create Instance from Attribute NONE");
+ return new Attribute(this, valueTypes);
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/ImagePath.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/ImagePath.java
new file mode 100644
index 0000000..b697bdc
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/ImagePath.java
@@ -0,0 +1,21 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class ImagePath extends ParticleAttributeValue {
+
+ private String imagePath;
+
+ public ImagePath() {
+ super(ParticleAttributeValueType.IMAGE_PATH);
+ }
+
+ public String getImagePath() {
+ return imagePath;
+ }
+
+ public void setImagePath(String imagePath) {
+ this.imagePath = imagePath;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/Options.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/Options.java
new file mode 100644
index 0000000..11c5190
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/Options.java
@@ -0,0 +1,66 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Options extends ParticleAttributeValue {
+
+ private boolean attached;
+ private boolean continuous;
+ private boolean aligned;
+ private boolean additive;
+ private boolean behind;
+ private boolean premultipliedAlpha;
+
+ public Options() {
+ super(ParticleAttributeValueType.OPTIONS);
+ }
+
+ public boolean isAttached() {
+ return attached;
+ }
+
+ public void setAttached(boolean attached) {
+ this.attached = attached;
+ }
+
+ public boolean isContinuous() {
+ return continuous;
+ }
+
+ public void setContinuous(boolean continuous) {
+ this.continuous = continuous;
+ }
+
+ public boolean isAligned() {
+ return aligned;
+ }
+
+ public void setAligned(boolean aligned) {
+ this.aligned = aligned;
+ }
+
+ public boolean isAdditive() {
+ return additive;
+ }
+
+ public void setAdditive(boolean additive) {
+ this.additive = additive;
+ }
+
+ public boolean isBehind() {
+ return behind;
+ }
+
+ public void setBehind(boolean behind) {
+ this.behind = behind;
+ }
+
+ public boolean isPremultipliedAlpha() {
+ return premultipliedAlpha;
+ }
+
+ public void setPremultipliedAlpha(boolean premultipliedAlpha) {
+ this.premultipliedAlpha = premultipliedAlpha;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/ParticleAttributeValue.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/ParticleAttributeValue.java
new file mode 100644
index 0000000..e366b3f
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/ParticleAttributeValue.java
@@ -0,0 +1,17 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public abstract class ParticleAttributeValue {
+
+ private ParticleAttributeValueType type;
+
+ public ParticleAttributeValue(ParticleAttributeValueType type) {
+ this.type = type;
+ }
+
+ public ParticleAttributeValueType getType() {
+ return type;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/ParticleAttributeValueType.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/ParticleAttributeValueType.java
new file mode 100644
index 0000000..60321aa
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/ParticleAttributeValueType.java
@@ -0,0 +1,28 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public enum ParticleAttributeValueType {
+
+ RANGE, TIMELINE, TINT_TIMELINE, SPAWN_SHAPE, OPTIONS, IMAGE_PATH;
+
+ public ParticleAttributeValue createInstance() {
+ switch (this) {
+ case RANGE:
+ return new Range();
+ case TIMELINE:
+ return new Timeline();
+ case TINT_TIMELINE:
+ return new TintTimeline();
+ case SPAWN_SHAPE:
+ return new SpawnShape();
+ case OPTIONS:
+ return new Options();
+ case IMAGE_PATH:
+ return new ImagePath();
+ }
+ return null;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/Range.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/Range.java
new file mode 100644
index 0000000..c0eb2d1
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/Range.java
@@ -0,0 +1,82 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+import java.util.Random;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Range extends ParticleAttributeValue {
+
+ private float lowMin;
+ private float lowMax;
+ private float highMin;
+ private float highMax;
+
+ public Range() {
+ super(ParticleAttributeValueType.RANGE);
+ }
+
+ public Range createNormalizedInstance(Random random) {
+ Range range = new Range();
+ float high = createHighValue(random);
+ range.setHighMax(high);
+ range.setHighMin(high);
+ float low = createLowValue(random);
+ range.setLowMax(low);
+ range.setLowMin(low);
+ return range;
+ }
+
+ public float createValue(Random random, float mix) {
+ if (mix == 1)
+ return createHighValue(random);
+ else if (mix == 0)
+ return createLowValue(random);
+ else {
+ float highValue = createHighValue(random);
+ float lowValue = createLowValue(random);
+ return mix * (highValue - lowValue) + lowValue;
+ }
+ }
+
+ private float createHighValue(Random random) {
+ float min = highMin;
+ float max = highMax;
+ if (min == max)
+ return min;
+ return random.nextFloat() * (max - min) + min;
+ }
+
+ private float createLowValue(Random random) {
+ float min = lowMin;
+ float max = lowMax;
+ if (min == max)
+ return min;
+ return random.nextFloat() * (max - min) + min;
+ }
+
+ public void setLowMin(float lowMin) {
+ this.lowMin = lowMin;
+ }
+
+ public void setLowMax(float lowMax) {
+ this.lowMax = lowMax;
+ }
+
+ public void setHighMin(float highMin) {
+ this.highMin = highMin;
+ }
+
+ public void setHighMax(float highMax) {
+ this.highMax = highMax;
+ }
+
+ private boolean isLowDifference() {
+ return lowMin - lowMax != 0;
+ }
+
+ private boolean isHighDifference() {
+ return highMin - highMax != 0;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/SpawnShape.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/SpawnShape.java
new file mode 100644
index 0000000..4de13d4
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/SpawnShape.java
@@ -0,0 +1,38 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class SpawnShape extends ParticleAttributeValue {
+
+ public enum Shape {
+ POINT("point"), SQUARE("square"), LINE("line");
+
+ private String name;
+
+ Shape(String name) {
+ this.name = name;
+ }
+
+ public static Shape byName(String text) throws Exception {
+ for (Shape shape : values())
+ if (shape.name.equals(text))
+ return shape;
+ throw new Exception("spawnShape with name \"" + text + "\" does not exist");
+ }
+ }
+
+ private Shape shape;
+
+ public SpawnShape() {
+ super(ParticleAttributeValueType.SPAWN_SHAPE);
+ }
+
+ public Shape getShape() {
+ return shape;
+ }
+
+ public void setShape(Shape shape) {
+ this.shape = shape;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/Timeline.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/Timeline.java
new file mode 100644
index 0000000..ae45d9e
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/Timeline.java
@@ -0,0 +1,54 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Timeline extends ParticleAttributeValue {
+
+ private List points = new ArrayList<>();
+
+ public Timeline() {
+ super(ParticleAttributeValueType.TIMELINE);
+ }
+
+ public void setValueOfPoint(int index, float value) {
+ if (points.size() <= index) {
+ points.add(new TimelinePoint());
+ }
+ points.get(index).setValue(value);
+ }
+
+ public void setTimeOfPoint(int index, float time) {
+ if (points.size() <= index) {
+ points.add(new TimelinePoint());
+ }
+ points.get(index).setTime(time);
+ }
+
+ public float getValueAtTime(float time) {
+ TimelinePoint left = null, right = null;
+ for (TimelinePoint point : points) {
+ if (point.getTime() <= time) {
+ if (left == null || left.getTime() < point.getTime())
+ left = point;
+ } else if (right == null || right.getTime() > point.getTime())
+ right = point;
+ }
+ if (left != null) {
+ if (right != null) {
+ float leftDist = Math.abs(left.getTime() - time);
+ float rightDist = Math.abs(right.getTime() - time);
+ float totalDist = leftDist + rightDist;
+ float leftMultiplier = 1 - (leftDist / totalDist);
+ float rightMultiplier = 1 - (rightDist / totalDist);
+ return left.getValue() * leftMultiplier + right.getValue() * rightMultiplier;
+ }
+ return left.getValue();
+ }
+ return 0;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TimelinePoint.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TimelinePoint.java
new file mode 100644
index 0000000..61804b4
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TimelinePoint.java
@@ -0,0 +1,23 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+public class TimelinePoint {
+
+ private float time, value;
+
+ public float getTime() {
+ return time;
+ }
+
+ public void setTime(float time) {
+ this.time = time;
+ }
+
+ public float getValue() {
+ return value;
+ }
+
+ public void setValue(float value) {
+ this.value = value;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TimelineRange.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TimelineRange.java
new file mode 100644
index 0000000..abcecb1
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TimelineRange.java
@@ -0,0 +1,31 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class TimelineRange {
+
+ private Timeline timeline;
+ private Range range;
+
+ public TimelineRange(Timeline timeline, Range range) {
+ this.timeline = timeline;
+ this.range = range;
+ }
+
+ public Timeline getTimeline() {
+ return timeline;
+ }
+
+ public void setTimeline(Timeline timeline) {
+ this.timeline = timeline;
+ }
+
+ public Range getRange() {
+ return range;
+ }
+
+ public void setRange(Range range) {
+ this.range = range;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TintTimeline.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TintTimeline.java
new file mode 100644
index 0000000..21df35e
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TintTimeline.java
@@ -0,0 +1,58 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+import de.frajul.endlessroll.data.Color;
+import de.frajul.endlessroll.main.GameLog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class TintTimeline extends ParticleAttributeValue {
+
+ private List points = new ArrayList<>();
+
+ public TintTimeline() {
+ super(ParticleAttributeValueType.TINT_TIMELINE);
+ }
+
+ public void setValueOfPoint(int index, int colorIndex, float value) {
+ if (points.size() <= index) {
+ points.add(new TintTimelinePoint());
+ }
+ points.get(index).setValue(colorIndex, value);
+ }
+
+ public void setTimeOfPoint(int index, float time) {
+ if (points.size() <= index) {
+ points.add(new TintTimelinePoint());
+ }
+ points.get(index).setTime(time);
+ }
+
+ public Color getValueAtTime(float time) {
+ TintTimelinePoint left = null, right = null;
+ for (TintTimelinePoint point : points) {
+ if (point.getTime() <= time) {
+ if (left == null || left.getTime() < point.getTime())
+ left = point;
+ } else if (right == null || right.getTime() > point.getTime())
+ right = point;
+ }
+ if (left != null) {
+ if (right != null) {
+ float leftDist = Math.abs(left.getTime() - time);
+ float rightDist = Math.abs(right.getTime() - time);
+ float totalDist = leftDist + rightDist;
+ float leftMultiplier = leftDist / totalDist;
+ float rightMultiplier = rightDist / totalDist;
+
+ return left.getColor().interpolate(leftMultiplier,rightMultiplier, right.getColor());
+ }
+ return left.getColor();
+ }
+ return new Color();
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TintTimelinePoint.java b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TintTimelinePoint.java
new file mode 100644
index 0000000..7f98332
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/particles/attributes/attributeValues/TintTimelinePoint.java
@@ -0,0 +1,33 @@
+package de.frajul.endlessroll.entities.particles.attributes.attributeValues;
+
+import de.frajul.endlessroll.data.Color;
+
+public class TintTimelinePoint {
+
+ private float time;
+ private Color color;
+
+ public float getTime() {
+ return time;
+ }
+
+ public void setTime(float time) {
+ this.time = time;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+
+ public void setValue(int colorIndex, float value) {
+ if (color == null)
+ color = new Color();
+ if (colorIndex == 0)
+ color.setR(value);
+ else if (colorIndex == 1)
+ color.setG(value);
+ else if (colorIndex == 2)
+ color.setB(value);
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/textures/Texture.java b/app/src/main/java/de/frajul/endlessroll/entities/textures/Texture.java
new file mode 100644
index 0000000..daeddd2
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/textures/Texture.java
@@ -0,0 +1,36 @@
+package de.frajul.endlessroll.entities.textures;
+
+/**
+ * Created by Julian on 11.12.2015.
+ */
+public class Texture {
+
+ private int id;
+ private int atlasWidth;
+ private int atlasHeight;
+
+ public Texture(int id, int atlasWidth, int atlasHeight) {
+ this.id = id;
+ this.atlasWidth = atlasWidth;
+ this.atlasHeight = atlasHeight;
+ }
+
+ public Texture(Texture other) {
+ this.id = other.getId();
+ this.atlasWidth = other.getAtlasWidth();
+ this.atlasHeight = other.getAtlasHeight();
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public int getAtlasWidth() {
+ return atlasWidth;
+ }
+
+ public int getAtlasHeight() {
+ return atlasHeight;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/textures/TextureLoader.java b/app/src/main/java/de/frajul/endlessroll/entities/textures/TextureLoader.java
new file mode 100644
index 0000000..b2860ce
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/textures/TextureLoader.java
@@ -0,0 +1,59 @@
+package de.frajul.endlessroll.entities.textures;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+
+import java.io.InputStream;
+
+import de.frajul.endlessroll.main.GameLog;
+
+/**
+ * Created by Julian on 26.11.2015.
+ */
+public class TextureLoader {
+
+ private Context context;
+
+ public TextureLoader(Context context) {
+ this.context = context;
+ }
+
+ public int loadTextureId(int texture, boolean isAtlas) {
+ Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), texture);
+ return loadTextureId(bitmap, isAtlas);
+ }
+
+ public Texture loadTexture(String inAssetsLocation) throws Exception {
+ InputStream is = context.getAssets().open(inAssetsLocation);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inScaled = false;
+ Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
+ return new Texture(loadTextureId(bitmap, false), 1, 1);
+ }
+
+ private int loadTextureId(Bitmap bitmap, boolean isAtlas) {
+ int id = genTexture();
+
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_NEAREST);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_NEAREST);
+
+ GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
+
+ bitmap.recycle();
+ GameLog.d("Texture " + id + " successfully loaded");
+ return id;
+ }
+
+ private int genTexture() {
+ int[] idField = new int[1];
+ GLES20.glGenTextures(1, idField, 0);
+ return idField[0];
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/textures/TexturePack.java b/app/src/main/java/de/frajul/endlessroll/entities/textures/TexturePack.java
new file mode 100644
index 0000000..04b43c9
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/textures/TexturePack.java
@@ -0,0 +1,45 @@
+package de.frajul.endlessroll.entities.textures;
+
+import android.content.Context;
+import android.support.annotation.DrawableRes;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.entities.shapes.PlayerShape;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.levels.worlds.World;
+
+/**
+ * Created by Julian on 05.12.2015.
+ */
+public class TexturePack {
+
+ private TextureLoader loader;
+
+ public final Texture goal;
+ public final Texture playerArrow;
+ public final Texture star;
+ public final Texture energy;
+
+ public TexturePack(Context context) {
+ loader = new TextureLoader(context);
+ goal = loadTexture(R.drawable.guis_goal);
+ playerArrow = loadTexture(R.drawable.guis_playerarrow);
+
+ star = loadTexture(R.drawable.currency_star);
+ energy = loadAtlas(R.drawable.currency_energy_atlas, 2, 2);
+
+ PlayerShape.loadAllTextures(this);
+ ToolType.loadAllToolTextures(this);
+ World.loadAllSpecificTextures(this);
+ }
+
+ public Texture loadTexture(@DrawableRes int id) {
+ int texId = loader.loadTextureId(id, false);
+ return new Texture(texId, 1, 1);
+ }
+
+ public Texture loadAtlas(@DrawableRes int id, int atlasWidth, int atlasHeight) {
+ int texId = loader.loadTextureId(id, true);
+ return new Texture(texId, atlasWidth, atlasHeight);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tileLists/Ceiling.java b/app/src/main/java/de/frajul/endlessroll/entities/tileLists/Ceiling.java
new file mode 100644
index 0000000..85947ae
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tileLists/Ceiling.java
@@ -0,0 +1,12 @@
+package de.frajul.endlessroll.entities.tileLists;
+
+import de.frajul.endlessroll.entities.textures.Texture;
+
+@SuppressWarnings("serial")
+public class Ceiling extends TileList {
+
+ public Ceiling(Texture texture) {
+ super(Type.CEILING, texture);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tileLists/Terrain.java b/app/src/main/java/de/frajul/endlessroll/entities/tileLists/Terrain.java
new file mode 100644
index 0000000..c328ac9
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tileLists/Terrain.java
@@ -0,0 +1,12 @@
+package de.frajul.endlessroll.entities.tileLists;
+
+import de.frajul.endlessroll.entities.textures.Texture;
+
+@SuppressWarnings("serial")
+public class Terrain extends TileList {
+
+ public Terrain(Texture texture) {
+ super(TileList.Type.TERRAIN, texture);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tileLists/Tile.java b/app/src/main/java/de/frajul/endlessroll/entities/tileLists/Tile.java
new file mode 100644
index 0000000..8311d16
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tileLists/Tile.java
@@ -0,0 +1,32 @@
+package de.frajul.endlessroll.entities.tileLists;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.entities.textures.Texture;
+import de.frajul.endlessroll.levels.TileData;
+
+/**
+ * Created by Julian on 18.12.2015.
+ */
+
+public class Tile extends Entity {
+
+ public Tile(TileList.Type type, Texture texture, float edge, TileData data) {
+ this(type, texture, edge, data.getX(), data.getWidth());
+ }
+
+ public Tile(TileList.Type type, Texture texture, float edge, float x, float width) {
+ super(texture, new Vector(), width, 0);
+ super.height = type.calculateTileHeightFromEdge(edge);
+ super.position.x = x;
+ switch (type) {
+ case TERRAIN:
+ super.position.y = edge - super.height / 2;
+ break;
+ case CEILING:
+ super.position.y = edge + super.height / 2;
+ break;
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tileLists/TileList.java b/app/src/main/java/de/frajul/endlessroll/entities/tileLists/TileList.java
new file mode 100644
index 0000000..ba47f49
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tileLists/TileList.java
@@ -0,0 +1,79 @@
+package de.frajul.endlessroll.entities.tileLists;
+
+import de.frajul.endlessroll.data.SynchronizedArrayList;
+import de.frajul.endlessroll.entities.textures.Texture;
+import de.frajul.endlessroll.levels.TileData;
+import de.frajul.endlessroll.levels.worlds.World;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class TileList extends SynchronizedArrayList {
+
+ public enum Type {
+ TERRAIN, CEILING;
+
+ public float calculateTileHeightFromEdge(float edge) {
+ switch (this) {
+ case TERRAIN:
+ return 1 + edge;
+ case CEILING:
+ return 1 - edge;
+ }
+ return 0;
+ }
+
+ }
+
+ private Type type;
+ private Texture texture;
+ private float edge;
+ private boolean endless;
+
+ public TileList(Type type, Texture texture) {
+ this.type = type;
+ this.texture = texture;
+ }
+
+ public void loadData(World world, float edge, List tileData) {
+ this.texture = world.getTerrainTexture();
+ if (type == Type.CEILING)
+ this.texture = world.getCeilingTexture();
+ this.endless = false;
+ super.clear();
+ for (TileData data : tileData)
+ super.add(new Tile(type, texture, edge, data));
+ this.edge = edge;
+ if (edge >= 1 || edge <= -1)
+ super.clear();
+ }
+
+ public void createEndless(World world, float edge) {
+ loadData(world, edge, new ArrayList());
+ super.add(createEndlessTile(0));
+ this.endless = true;
+ }
+
+ public void update(float cameraX) {
+ if (!super.isEmpty()) {
+ if (endless) {
+ Tile last = super.get(super.size() - 1);
+ if (last.getRightEdge() - cameraX < 3)
+ super.add(createEndlessTile(last.getRightEdge() + 2.5f));
+ }
+ if (super.get(0).getRightEdge() - cameraX < -3) {
+ super.remove(0);
+ }
+ }
+ }
+
+ private Tile createEndlessTile(float x) {
+ return new Tile(type, texture, edge, x, 5);
+ }
+
+ public float getEdge() {
+ return edge;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tools/Bomb.java b/app/src/main/java/de/frajul/endlessroll/entities/tools/Bomb.java
new file mode 100644
index 0000000..34d0761
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tools/Bomb.java
@@ -0,0 +1,67 @@
+package de.frajul.endlessroll.entities.tools;
+
+import java.util.List;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.DestroyEffect;
+import de.frajul.endlessroll.entities.Obstacle;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collision.CollisionDetector;
+import de.frajul.endlessroll.entities.collision.geometry.Geometry;
+import de.frajul.endlessroll.entities.collision.geometry.Quad;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 20.02.2016.
+ */
+public class Bomb extends Tool {
+
+ public final float RANGE = 1;
+ private float delta;
+ private boolean exploding = false;
+
+ public Bomb(Vector position) {
+ super(ToolType.BOMB, position, .29f, .29f, false, false);
+ animation.setIndexSequence(new int[]{0, 1, 2});
+ animation.setLooping(false);
+ animation.setRequiredDelta(
+ (int) (ToolType.BOMB.getCurrentUpgradeValue(ToolUpgradeType.DURATION) / 3));
+ }
+
+ @Override
+ public void update(Timer timer) {
+ super.update(timer);
+ delta += timer.getFrameTimeSeconds();
+ if (delta >= ToolType.BOMB.getCurrentUpgradeValue(ToolUpgradeType.DURATION))
+ exploding = true;
+ }
+
+ @Override
+ public void onPlayerCollision(Player player, Timer timer) {
+
+ }
+
+ @Override
+ protected Geometry createWorldCollisionBounds() {
+ return this;
+ }
+
+ @Override
+ protected Geometry createPlayerCollisionBounds() {
+ return this;
+ }
+
+ public boolean isExploding() {
+ return exploding;
+ }
+
+ public void explode(List obstacles, CollisionDetector detector) {
+ float range = RANGE * ToolType.BOMB.getCurrentUpgradeValue(ToolUpgradeType.RANGE) / 100;
+ Quad explosionRange = new Quad(new Vector(super.getPosition()), range, range);
+ for (Obstacle obstacle : obstacles) {
+ if (detector.isQuadQuadCollision(obstacle, explosionRange))
+ obstacle.destroy(DestroyEffect.EXPLOSION);
+ }
+ super.destroy(DestroyEffect.EXPLOSION);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tools/Magnet.java b/app/src/main/java/de/frajul/endlessroll/entities/tools/Magnet.java
new file mode 100644
index 0000000..95d2234
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tools/Magnet.java
@@ -0,0 +1,59 @@
+package de.frajul.endlessroll.entities.tools;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.DestroyEffect;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collision.geometry.Circle;
+import de.frajul.endlessroll.entities.collision.geometry.Geometry;
+import de.frajul.endlessroll.entities.particles.ParticleSource;
+import de.frajul.endlessroll.entities.particles.ParticleSystem;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 11.02.2016.
+ */
+public class Magnet extends Tool {
+
+ private ParticleSource particleSource;
+
+ public Magnet(Vector position, ParticleSystem particleSystem) {
+ super(ToolType.MAGNET, position, .24f, .24f, false, false);
+ animation.setRequiredDelta(300);
+ animation.setIndexSequence(new int[]{1, 1, 0});
+ animation.setLooping(true);
+ super.setFloating(true);
+ particleSource = new ParticleSource(new Vector(position), particleSystem.magnet);
+ particleSource.start();
+ }
+
+ @Override
+ public void destroy(DestroyEffect destroyEffect) {
+ super.destroy(destroyEffect);
+ particleSource.kill();
+ }
+
+ @Override
+ public void onPlayerCollision(Player player, Timer timer) {
+ float fromPlayerDistance = player.getPosition().vectorTo(super.getPosition()).length();
+ float fromPlayerDistanceGreaterFour = Math.max(fromPlayerDistance, 0.4f);
+ float fromPlayerYDistance = super.getPosition().y - player.getPosition().y;
+ float force = 0.0000012f / (fromPlayerDistanceGreaterFour * fromPlayerDistanceGreaterFour);
+ force *= ToolType.MAGNET.getCurrentUpgradeValue(ToolUpgradeType.FORCE) / 100;
+ force *= timer.getFrameTimeSeconds();
+
+ if (fromPlayerYDistance < 0) {
+ force = -force;
+ }
+ player.addForce(force);
+ }
+
+ @Override
+ protected Geometry createWorldCollisionBounds() {
+ return this;
+ }
+
+ @Override
+ protected Geometry createPlayerCollisionBounds() {
+ return new Circle(super.getPosition(), 2);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tools/Ramp.java b/app/src/main/java/de/frajul/endlessroll/entities/tools/Ramp.java
new file mode 100644
index 0000000..da0eb88
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tools/Ramp.java
@@ -0,0 +1,64 @@
+package de.frajul.endlessroll.entities.tools;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collision.geometry.Geometry;
+import de.frajul.endlessroll.entities.collision.geometry.Triangle;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 29.11.2015.
+ */
+public class Ramp extends Tool {
+
+ public Ramp(Vector position) {
+ super(ToolType.RAMP, position, .4f, .35f, true, true);
+ animation.setLooping(true);
+ }
+
+ public float getGradient() {
+ return super.getHeight() / super.getWidth();
+ }
+
+ public float getHeightAt(float x, boolean clamp) {
+ float ratio = (x - getLeftEdge()) / super.getWidth();
+ if (clamp) {
+ if (ratio < 0)
+ return getBottomEdge();
+ if (ratio > 1)
+ return getTopEdge();
+ }
+ return getBottomEdge() + super.getHeight() * ratio;
+ }
+
+ @Override
+ public void onPlayerCollision(Player player, Timer timer) {
+ float necessaryY = calcNecessaryPlayerY(player);
+ player.getPosition().y = necessaryY;
+ float acceleration = player.getMovement().x * getGradient();
+
+ player.setGravityForce(0);
+ player.getMovement().setY(0);
+ player.addForce(acceleration);
+ }
+
+ private float calcNecessaryPlayerY(Player player) {
+ float normalM = -1 / getGradient();
+ Vector normalToCircleCenter = new Vector(-1, -normalM).normalize();
+ normalToCircleCenter.mul(player.RADIUS);
+ float normalX = player.getPosition().x - normalToCircleCenter.x;
+ float normalY = getHeightAt(normalX, false);
+ return normalY + normalToCircleCenter.y;
+ }
+
+ @Override
+ protected Geometry createWorldCollisionBounds() {
+ return new Triangle(this);
+ }
+
+ @Override
+ protected Geometry createPlayerCollisionBounds() {
+ return new Triangle(this);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tools/Spring.java b/app/src/main/java/de/frajul/endlessroll/entities/tools/Spring.java
new file mode 100644
index 0000000..8796445
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tools/Spring.java
@@ -0,0 +1,48 @@
+package de.frajul.endlessroll.entities.tools;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collision.geometry.Geometry;
+import de.frajul.endlessroll.entities.collision.geometry.Quad;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 04.01.2016.
+ */
+public class Spring extends Tool {
+
+ private boolean hasYetCollided = false;
+
+ public Spring(Vector position) {
+ super(ToolType.SPRING, position, .3f, .35f, true, true);
+ animation.setIndexSequence(new int[]{1, 0, 0, 3, 3, 3, 1});
+ animation.setRequiredDelta(80);
+ }
+
+ @Override
+ public void update(Timer timer) {
+ if (hasYetCollided)
+ super.update(timer);
+ }
+
+ @Override
+ public void onPlayerCollision(Player player, Timer timer) {
+ if (!hasYetCollided) {
+ hasYetCollided = true;
+
+ player.clearAllForces();
+ player.getMovement().setY(0);
+ player.addForce(.0022f);
+ }
+ }
+
+ @Override
+ protected Geometry createWorldCollisionBounds() {
+ return this;
+ }
+
+ @Override
+ protected Geometry createPlayerCollisionBounds() {
+ return new Quad(super.getPosition(), .2f, .1f);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tools/Tool.java b/app/src/main/java/de/frajul/endlessroll/entities/tools/Tool.java
new file mode 100644
index 0000000..7ab3214
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tools/Tool.java
@@ -0,0 +1,57 @@
+package de.frajul.endlessroll.entities.tools;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.AnimatedEntity;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collision.geometry.Geometry;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 04.01.2016.
+ */
+public abstract class Tool extends AnimatedEntity {
+
+ private boolean placedByRightEdge;
+ private boolean updateBounds;
+ private Geometry worldCollisionBounds;
+ private Geometry playerCollisionBounds;
+ private boolean floating = false;
+
+ public Tool(ToolType type, Vector position, float width, float height, boolean updateBounds, boolean placedByRightEdge) {
+ super(type.getToolTexture(), position, width, height);
+ this.updateBounds = updateBounds;
+ this.placedByRightEdge = placedByRightEdge;
+ worldCollisionBounds = createWorldCollisionBounds();
+ playerCollisionBounds = createPlayerCollisionBounds();
+ }
+
+ public abstract void onPlayerCollision(Player player, Timer timer);
+
+ protected abstract Geometry createWorldCollisionBounds();
+
+ protected abstract Geometry createPlayerCollisionBounds();
+
+ public Geometry getWorldCollisionBounds() {
+ if (updateBounds)
+ return createWorldCollisionBounds();
+ return worldCollisionBounds;
+ }
+
+ public Geometry getPlayerCollisionBounds() {
+ if (updateBounds)
+ return createPlayerCollisionBounds();
+ return playerCollisionBounds;
+ }
+
+ public void setFloating(boolean floating) {
+ this.floating = floating;
+ }
+
+ public boolean isFloating() {
+ return floating;
+ }
+
+ public boolean isPlacedByRightEdge() {
+ return placedByRightEdge;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tools/ToolSlot.java b/app/src/main/java/de/frajul/endlessroll/entities/tools/ToolSlot.java
new file mode 100644
index 0000000..4ff3dd4
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tools/ToolSlot.java
@@ -0,0 +1,42 @@
+package de.frajul.endlessroll.entities.tools;
+
+import de.frajul.endlessroll.R;
+
+/**
+ * Created by Julian on 16.07.2016.
+ */
+public class ToolSlot {
+
+ private ToolType toolType;
+ private boolean locked;
+
+ public ToolSlot(ToolType toolType, boolean locked) {
+ this.toolType = toolType;
+ this.locked = locked;
+ }
+
+ public ToolType getToolType() {
+ return toolType;
+ }
+
+ public int getDrawable() {
+ if (locked)
+ return R.drawable.tools_button_locked;
+ else if(toolType != null)
+ return toolType.getButtonDrawable();
+ else
+ return R.drawable.tools_button_empty;
+ }
+
+ public void setToolType(ToolType toolType) {
+ this.toolType = toolType;
+ }
+
+ public boolean isLocked() {
+ return locked;
+ }
+
+ public void setLocked(boolean locked) {
+ this.locked = locked;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/entities/tools/ToolType.java b/app/src/main/java/de/frajul/endlessroll/entities/tools/ToolType.java
new file mode 100644
index 0000000..b50e919
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/entities/tools/ToolType.java
@@ -0,0 +1,194 @@
+package de.frajul.endlessroll.entities.tools;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+
+import java.util.Arrays;
+import java.util.List;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.particles.ParticleSystem;
+import de.frajul.endlessroll.entities.textures.Texture;
+import de.frajul.endlessroll.entities.textures.TexturePack;
+import de.frajul.endlessroll.sounds.SoundManager;
+
+public enum ToolType {
+
+ //Check newInstance when new Tool is added!
+ RAMP(R.string.tool_name_ramp, R.string.tool_description_ramp, R.drawable.tools_ramp,
+ R.drawable.tools_ramp_button, R.raw.ramp, 0, 1, 5,
+ new ToolUpgrade(ToolUpgradeType.COOLDOWN, 3000, 1000)),
+ SPRING(R.string.tool_name_spring, R.string.tool_description_spring, R.drawable.tools_spring,
+ R.drawable.tools_spring_button, R.raw.ramp, 5, 2, 5,
+ new ToolUpgrade(ToolUpgradeType.COOLDOWN, 4000, 2000)),
+ BOMB(R.string.tool_name_bomb, R.string.tool_description_bomb, R.drawable.tools_bomb,
+ R.drawable.tools_bomb_button, R.raw.ramp, 12, 4, 5,
+ new ToolUpgrade(ToolUpgradeType.COOLDOWN, 6000, 4000),
+ new ToolUpgrade(ToolUpgradeType.DURATION, 1200, 400),
+ new ToolUpgrade(ToolUpgradeType.RANGE, 100, 200)),
+ MAGNET(R.string.tool_name_magnet, R.string.tool_description_magnet, R.drawable.tools_magnet,
+ R.drawable.tools_magnet_button, R.raw.ramp, 10, 4, 5,
+ new ToolUpgrade(ToolUpgradeType.COOLDOWN, 6000, 4000),
+ new ToolUpgrade(ToolUpgradeType.FORCE, 100, 500)),
+ POWER_MUSHROOM(R.string.tool_name_power_mushroom, R.string.tool_description_power_mushroom,
+ R.drawable.tools_power_mushroom, R.drawable.tools_power_mushroom_button, R.raw.ramp, 5,
+ 5, 7, new ToolUpgrade(ToolUpgradeType.COOLDOWN, 15000, 11000),
+ new ToolUpgrade(ToolUpgradeType.DURATION, 5000, 10000)),
+ STASIS(R.string.tool_name_stasis, R.string.tool_description_stasis, R.drawable.tools_stasis,
+ R.drawable.tools_stasis_button, R.raw.ramp, 15, 6, 6,
+ new ToolUpgrade(ToolUpgradeType.COOLDOWN, 8000, 5000),
+ new ToolUpgrade(ToolUpgradeType.FORCE, 1, 2),
+ new ToolUpgrade(ToolUpgradeType.SIZE, 100, 200));
+
+ @StringRes
+ private final int name;
+ @StringRes
+ private final int description;
+ private final int toolTextureId;
+ private final int buttonDrawable;
+ private final int placingSoundId;
+ private final int buyPrice;
+ private final int upgradePrice;
+ private final int maxUpgradeLevel;
+ private final List upgrades;
+
+ private Texture toolTexture = null;
+ private int placingSound = -1;
+ private boolean bought;
+ private int currentUpgradeLevel = 1;
+
+ ToolType(@StringRes int name, @StringRes int description, int toolTextureId, int buttonDrawable, int placingSoundId, int buyPrice, int upgradePrice, int maxUpgradeLevel, ToolUpgrade... upgrades) {
+ this.name = name;
+ this.description = description;
+ this.toolTextureId = toolTextureId;
+ this.buttonDrawable = buttonDrawable;
+ this.placingSoundId = placingSoundId;
+ this.buyPrice = buyPrice;
+ this.upgradePrice = upgradePrice;
+ this.maxUpgradeLevel = maxUpgradeLevel;
+ this.upgrades = Arrays.asList(upgrades);
+ }
+
+ @Nullable
+ public Tool newInstance(Vector position, ParticleSystem particleSystem) {
+ Tool tool = null;
+ switch (this) {
+ case RAMP:
+ tool = new Ramp(position);
+ break;
+ case SPRING:
+ tool = new Spring(position);
+ break;
+ case MAGNET:
+ tool = new Magnet(position, particleSystem);
+ break;
+ case BOMB:
+ tool = new Bomb(position);
+ break;
+ case POWER_MUSHROOM:
+ tool = new PowerMushroom(position);
+ break;
+ case STASIS:
+ tool = new Stasis(position,particleSystem);
+ break;
+ }
+ if (tool != null && tool.isPlacedByRightEdge())
+ tool.move(new Vector(-tool.getWidth() / 2, 0));
+ return tool;
+ }
+
+ public static void loadAllPlacingSounds(SoundManager soundManager) {
+ for (ToolType type : values())
+ type.loadPlacingSound(soundManager);
+ }
+
+ public static void loadAllToolTextures(TexturePack texturePack) {
+ for (ToolType type : values())
+ type.loadToolTexture(texturePack);
+ }
+
+ private void loadPlacingSound(SoundManager soundManager) {
+ if (placingSoundId == -1)
+ return;
+ placingSound = soundManager.loadSound(placingSoundId);
+ }
+
+ private void loadToolTexture(TexturePack texturePack) {
+ if (toolTextureId == -1)
+ return;
+ if (this != POWER_MUSHROOM)
+ toolTexture = texturePack.loadAtlas(toolTextureId, 2, 2);
+ else
+ toolTexture = texturePack.loadTexture(toolTextureId);
+ }
+
+ public Texture getToolTexture() {
+ return toolTexture;
+ }
+
+ public int getButtonDrawable() {
+ return buttonDrawable;
+ }
+
+ public int getPlacingSound() {
+ return placingSound;
+ }
+
+ @StringRes
+ public int getName() {
+ return name;
+ }
+
+ @StringRes
+ public int getDescription() {
+ return description;
+ }
+
+ public void setBought(boolean bought) {
+ this.bought = bought;
+ }
+
+ public boolean isBought() {
+ if (this == RAMP)
+ bought = true;
+ return bought;
+ }
+
+ public void upgrade() {
+ currentUpgradeLevel++;
+ }
+
+ public void reset() {
+ setBought(false);
+ currentUpgradeLevel = 1;
+ }
+
+ public int getBuyPrice() {
+ return buyPrice;
+ }
+
+ public int getUpgradePrice() {
+ return upgradePrice + currentUpgradeLevel - 1;
+ }
+
+ public int getCurrentUpgradeLevel() {
+ return currentUpgradeLevel;
+ }
+
+ public void setCurrentUpgradeLevel(int currentUpgradeLevel) {
+ this.currentUpgradeLevel = currentUpgradeLevel;
+ }
+
+ public float getCurrentUpgradeValue(ToolUpgradeType type) {
+ for (ToolUpgrade upgrade : upgrades)
+ if (upgrade.getType().equals(type))
+ return upgrade.getValueAtLevel(currentUpgradeLevel, maxUpgradeLevel);
+ return -1;
+ }
+
+ public boolean isAtMaxUpgradeLevel() {
+ return currentUpgradeLevel == maxUpgradeLevel;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/levels/Gap.java b/app/src/main/java/de/frajul/endlessroll/levels/Gap.java
new file mode 100644
index 0000000..ecd74f1
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/levels/Gap.java
@@ -0,0 +1,22 @@
+package de.frajul.endlessroll.levels;
+
+import org.simpleframework.xml.Attribute;
+
+/**
+ * Created by Julian on 27.11.2015.
+ */
+public class Gap {
+
+ @Attribute
+ private float leftEdge;
+ @Attribute
+ private float rightEdge;
+
+ public float getLeftEdge() {
+ return leftEdge;
+ }
+
+ public float getRightEdge() {
+ return rightEdge;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/levels/Level.java b/app/src/main/java/de/frajul/endlessroll/levels/Level.java
new file mode 100644
index 0000000..e99b14e
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/levels/Level.java
@@ -0,0 +1,147 @@
+package de.frajul.endlessroll.levels;
+
+import org.simpleframework.xml.Attribute;
+import org.simpleframework.xml.Element;
+import org.simpleframework.xml.ElementList;
+
+import java.util.List;
+
+/**
+ * Created by Julian on 07.12.2015.
+ */
+public class Level {
+
+ @Attribute
+ private int packId;
+ @Attribute
+ private int id;
+ @Attribute
+ private float goalX;
+ @Attribute
+ private float startSpeed;
+ @Attribute
+ private float endSpeed;
+ @Attribute
+ private float terrainEdge;
+ @Attribute
+ private float ceilingEdge;
+ @ElementList
+ private List terrainTiles;
+ @ElementList
+ private List ceilingTiles;
+ @ElementList
+ private List obstacles;
+ @ElementList
+ private List stars;
+ @Element(required = false)
+ private PositionData energy;
+
+ private boolean finished = false;
+ private boolean locked = true;
+ private boolean[] collectedStars = {false, false, false};
+ private boolean energyCollected = false;
+
+ public int getPackId() {
+ return packId;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public float getGoalX() {
+ return goalX;
+ }
+
+ public float getStartSpeed() {
+ return startSpeed;
+ }
+
+ public float getEndSpeed() {
+ return endSpeed;
+ }
+
+ public float getTerrainEdge() {
+ return terrainEdge;
+ }
+
+ public float getCeilingEdge() {
+ return ceilingEdge;
+ }
+
+ public List getTerrainTiles() {
+ return terrainTiles;
+ }
+
+ public List getCeilingTiles() {
+ return ceilingTiles;
+ }
+
+ public List getObstacles() {
+ return obstacles;
+ }
+
+ public List getStars() {
+ return stars;
+ }
+
+ public PositionData getEnergyData() {
+ return energy;
+ }
+
+ public boolean isLocked() {
+ return locked;
+ }
+
+ public void setLocked(boolean locked) {
+ this.locked = locked;
+ }
+
+ public boolean isFinished() {
+ return finished;
+ }
+
+ public void setFinished(boolean finished) {
+ this.finished = finished;
+ }
+
+ public boolean isStarCollected(int index) {
+ return collectedStars[index];
+ }
+
+ public void setStarCollected(int index, boolean collected) {
+ collectedStars[index] = collected;
+ }
+
+ public String getCollectedStarCodeForSQL() {
+ String code = "";
+ for (int i = 0; i < 3; i++)
+ code += collectedStars[i] ? (i + 1) + "" : "";
+ return code;
+ }
+
+ public void setCollectedStarsFromSQL(String code) {
+ for (int i = 0; i < 3; i++)
+ collectedStars[i] = code.contains((i + 1) + "");
+ }
+
+ public boolean[] getCollectedStars() {
+ return collectedStars;
+ }
+
+ public boolean isEnergyCollected() {
+ return energyCollected;
+ }
+
+ public void setEnergyCollected(boolean collected) {
+ this.energyCollected = collected;
+ }
+
+ public void reset() {
+ finished = false;
+ locked = true;
+ collectedStars = new boolean[]{false, false, false};
+ energyCollected = false;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/levels/LevelManager.java b/app/src/main/java/de/frajul/endlessroll/levels/LevelManager.java
new file mode 100644
index 0000000..6405af4
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/levels/LevelManager.java
@@ -0,0 +1,117 @@
+package de.frajul.endlessroll.levels;
+
+import android.content.Context;
+
+import org.simpleframework.xml.Serializer;
+import org.simpleframework.xml.core.Persister;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.main.DataStorageHandler;
+import de.frajul.endlessroll.main.GameLog;
+import de.frajul.endlessroll.sqlDatabase.MyDatabase;
+
+public class LevelManager extends ArrayList {
+
+ public LevelManager(Context context, DataStorageHandler dataStorageHandler) throws Exception {
+ String[] worldNames = context.getResources().getStringArray(R.array.world_names);
+ String[] assets = context.getAssets().list("levelpacks");
+ for (String asset : assets) {
+ try {
+ LevelPack pack = loadLevelPack(context, "levelpacks/" + asset);
+ pack.setName(worldNames[pack.getId()]);
+
+ MyDatabase database = dataStorageHandler.getDatabase();
+ database.open();
+ for (Level level : pack.getLevels())
+ database.readLevelProgress(level);
+ database.readLevelPackLocked(pack);
+ database.close();
+ pack.tryToUnlockFirstLevel();
+ if (pack.getId() == 1)
+ pack.setLocked(false);
+ super.add(pack);
+ } catch (Exception e) {
+ GameLog.e(e);
+ }
+ }
+ sortPacks();
+ }
+
+ private void sortPacks() {
+ Collections.sort(this, packComparator);
+ }
+
+ private LevelPack loadLevelPack(Context context, String name) throws Exception {
+ try {
+ InputStream source = context.getAssets().open(name);
+ Serializer serializer = new Persister();
+ return serializer.read(LevelPack.class, source);
+ } catch (Exception e) {
+ throw new Exception("Could not load levelPack \"" + name + "\"", e);
+ }
+ }
+
+ public int getTotalCollectedStarCount(){
+ int count = 0;
+ for(LevelPack levelPack : this)
+ count += levelPack.getCollectedStarCount();
+ return count;
+ }
+
+ public int getTotalCollectedEnergyCount(){
+ int count = 0;
+ for(LevelPack levelPack : this)
+ count += levelPack.getCollectedEnergyCount();
+ return count;
+ }
+
+ public LevelPack getNextLevelPack(LevelPack currentPack) {
+ int searchedId = currentPack.getId() + 1;
+ for (LevelPack pack : this) {
+ if (pack.getId() == searchedId)
+ return pack;
+ }
+ return null;
+ }
+
+ public LevelPack getPackWithId(int id) {
+ for (LevelPack pack : this)
+ if (pack.getId() == id)
+ return pack;
+ return null;
+ }
+
+ public void reset() {
+ for (LevelPack pack : this) {
+ pack.reset();
+ if (pack.getId() == 1)
+ pack.setLocked(false);
+ }
+ }
+
+ private Comparator packComparator = new Comparator() {
+ @Override
+ public int compare(LevelPack lhs, LevelPack rhs) {
+ return lhs.getId() - rhs.getId();
+ }
+ };
+
+ //CHEAT
+ public void unlockAllPacks() {
+ for (LevelPack levelPack : this)
+ levelPack.setLocked(false);
+ }
+
+ public void unlockAllLevels() {
+ for (LevelPack levelPack : this) {
+ for (Level level : levelPack.getLevels())
+ level.setLocked(false);
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/levels/LevelPack.java b/app/src/main/java/de/frajul/endlessroll/levels/LevelPack.java
new file mode 100644
index 0000000..3d6b238
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/levels/LevelPack.java
@@ -0,0 +1,120 @@
+package de.frajul.endlessroll.levels;
+
+import org.simpleframework.xml.Attribute;
+import org.simpleframework.xml.Element;
+import org.simpleframework.xml.ElementList;
+import org.simpleframework.xml.Root;
+
+import java.util.List;
+
+import de.frajul.endlessroll.levels.worlds.World;
+
+/**
+ * Created by Julian on 07.12.2015.
+ */
+@Root
+public class LevelPack {
+
+ @Attribute
+ private int id;
+ @Element
+ private World world;
+ @ElementList
+ private List levels;
+
+ private String name;
+ private boolean locked = true;
+
+ public int getId() {
+ return id;
+ }
+
+ public List getLevels() {
+ return levels;
+ }
+
+ public World getWorld() {
+ return world;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getFinishedLevelCount() {
+ int count = 0;
+ for (Level level : levels)
+ if (level.isFinished())
+ count++;
+ return count;
+ }
+
+ public int getCollectedStarCount() {
+ int count = 0;
+ for (Level level : levels)
+ count += level.getCollectedStarCodeForSQL().length();
+ return count;
+ }
+
+ public int getAvailableStars() {
+ return levels.size() * 3;
+ }
+
+ public int getCollectedEnergyCount() {
+ int count = 0;
+ for (Level level : levels)
+ count += level.isEnergyCollected() ? 1 : 0;
+ return count;
+ }
+
+ public int getAvailableEnergy() {
+ return levels.size();
+ }
+
+ public void tryToUnlockFirstLevel() {
+ Level firstLevel = getLevel(1);
+ if (firstLevel != null)
+ firstLevel.setLocked(false);
+ }
+
+ public Level getLevel(int id) {
+ for (Level level : levels)
+ if (level.getId() == id)
+ return level;
+ return null;
+ }
+
+ public Level getNextLevel(Level currentLevel) {
+ return getLevel(currentLevel.getId() + 1);
+ }
+
+ public boolean isAllLevelsFinished() {
+ for (Level level : levels)
+ if (!level.isFinished())
+ return false;
+ return true;
+ }
+
+ public boolean isLastLevel(Level level) {
+ return getNextLevel(level) == null;
+ }
+
+ public void reset() {
+ for (Level level : levels)
+ level.reset();
+ setLocked(true);
+ tryToUnlockFirstLevel();
+ }
+
+ public void setLocked(boolean locked) {
+ this.locked = locked;
+ }
+
+ public boolean isLocked() {
+ return locked;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/levels/MoveComponent.java b/app/src/main/java/de/frajul/endlessroll/levels/MoveComponent.java
new file mode 100644
index 0000000..9a79808
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/levels/MoveComponent.java
@@ -0,0 +1,56 @@
+package de.frajul.endlessroll.levels;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.Vertex;
+
+import org.simpleframework.xml.Attribute;
+
+public class MoveComponent {
+
+ @Attribute
+ private float width;
+ @Attribute
+ private float height;
+ @Attribute
+ private float x;
+ @Attribute
+ private float y;
+ @Attribute
+ private float speed;
+
+ public float getWidth() {
+ return width;
+ }
+
+ public float getHeight() {
+ return height;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public float getSpeed() {
+ return speed;
+ }
+
+ public Vector getPositionOfVertex(Vertex vertex) {
+ float x = this.x + (width / 2f) * vertex.getX();
+ float y = this.y + (height / 2f) * vertex.getY();
+ return new Vector(x, y);
+ }
+
+ public void shrink(Vector value) {
+ this.width -= value.getX();
+ this.height -= value.getY();
+ if (width < 0)
+ width = 0;
+ if (height < 0)
+ height = 0;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/levels/ObstacleData.java b/app/src/main/java/de/frajul/endlessroll/levels/ObstacleData.java
new file mode 100644
index 0000000..23dff98
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/levels/ObstacleData.java
@@ -0,0 +1,93 @@
+package de.frajul.endlessroll.levels;
+
+import org.simpleframework.xml.Attribute;
+import org.simpleframework.xml.Element;
+
+/**
+ * Created by Julian on 07.12.2015.
+ */
+public class ObstacleData {
+
+ @Attribute
+ private boolean floating;
+ @Attribute
+ private boolean moving;
+ @Attribute
+ private boolean deadly;
+ @Attribute
+ private float leftEdge;
+ @Attribute
+ private float rightEdge;
+ @Attribute
+ private float height;
+ @Attribute
+ private float y;
+ @Element(required = false)
+ private MoveComponent moveComponent;
+
+ public boolean isFloating() {
+ return floating;
+ }
+
+ public boolean isMoving() {
+ return moving;
+ }
+
+ public boolean isDeadly() {
+ return deadly;
+ }
+
+ public float getX() {
+ return leftEdge + getWidth() / 2;
+ }
+
+ public float getWidth() {
+ return rightEdge - leftEdge;
+ }
+
+ public float getHeight() {
+ return height;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public MoveComponent getMoveComponent() {
+ return moveComponent;
+ }
+
+
+ //Only for glTestScreen
+ public void setFloating(boolean floating) {
+ this.floating = floating;
+ }
+
+ public void setMoving(boolean moving) {
+ this.moving = moving;
+ }
+
+ public void setDeadly(boolean deadly) {
+ this.deadly = deadly;
+ }
+
+ public void setLeftEdge(float leftEdge) {
+ this.leftEdge = leftEdge;
+ }
+
+ public void setRightEdge(float rightEdge) {
+ this.rightEdge = rightEdge;
+ }
+
+ public void setHeight(float height) {
+ this.height = height;
+ }
+
+ public void setY(float y) {
+ this.y = y;
+ }
+
+ public void setMoveComponent(MoveComponent moveComponent) {
+ this.moveComponent = moveComponent;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/levels/PositionData.java b/app/src/main/java/de/frajul/endlessroll/levels/PositionData.java
new file mode 100644
index 0000000..908ea4f
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/levels/PositionData.java
@@ -0,0 +1,23 @@
+package de.frajul.endlessroll.levels;
+
+import org.simpleframework.xml.Attribute;
+
+/**
+ * Created by Julian on 27.01.2017.
+ */
+
+public class PositionData {
+
+ @Attribute
+ private float x;
+ @Attribute
+ private float y;
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/levels/TileData.java b/app/src/main/java/de/frajul/endlessroll/levels/TileData.java
new file mode 100644
index 0000000..01ecad8
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/levels/TileData.java
@@ -0,0 +1,19 @@
+package de.frajul.endlessroll.levels;
+
+import org.simpleframework.xml.Attribute;
+
+public class TileData {
+
+ @Attribute
+ private float x;
+ @Attribute
+ private float width;
+
+ public float getX() {
+ return x;
+ }
+
+ public float getWidth() {
+ return width;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/levels/worlds/World.java b/app/src/main/java/de/frajul/endlessroll/levels/worlds/World.java
new file mode 100644
index 0000000..bf6ee07
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/levels/worlds/World.java
@@ -0,0 +1,80 @@
+package de.frajul.endlessroll.levels.worlds;
+
+import android.support.annotation.DrawableRes;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.entities.textures.Texture;
+import de.frajul.endlessroll.entities.textures.TexturePack;
+
+/**
+ * Created by Julian on 14.11.2016.
+ */
+
+public enum World {
+
+ GRASSLANDS("Grasslands", R.drawable.world_previews_grass, R.drawable.backgrounds_game_grass, R.drawable.terrain_t_grass, R.drawable.terrain_c_grass, R.drawable.obstacles_grass),
+ TESTCAVE("Testcave", R.drawable.world_previews_grass, R.drawable.backgrounds_game_cave, R.drawable.terrain_t_grass, R.drawable.terrain_c_grass, R.drawable.obstacles_grass),
+ ICY_MOUNTAINS("Icy Mountains", R.drawable.world_previews_grass, R.drawable.backgrounds_game_mountains, R.drawable.terrain_t_grass, R.drawable.terrain_c_grass, R.drawable.obstacles_grass);
+
+ private String name;
+ @DrawableRes
+ private int previewId;
+ @DrawableRes
+ private int backgroundId;
+ @DrawableRes
+ private int terrainId;
+ @DrawableRes
+ private int ceilingId;
+ @DrawableRes
+ private int obstacleId;
+
+ private Texture background;
+ private Texture terrain;
+ private Texture ceiling;
+ private Texture obstacle;
+
+ World(String name, @DrawableRes int previewId, @DrawableRes int backgroundId, @DrawableRes int terrainId, @DrawableRes int ceilingId, @DrawableRes int obstacleId) {
+ this.name = name;
+ this.previewId = previewId;
+ this.backgroundId = backgroundId;
+ this.terrainId = terrainId;
+ this.ceilingId = ceilingId;
+ this.obstacleId = obstacleId;
+ }
+
+ public static void loadAllSpecificTextures(TexturePack texturePack) {
+ for (World world : values())
+ world.loadSpecificTextures(texturePack);
+ }
+
+ private void loadSpecificTextures(TexturePack texturePack) {
+ background = texturePack.loadTexture(backgroundId);
+ terrain = texturePack.loadTexture(terrainId);
+ ceiling = texturePack.loadTexture(ceilingId);
+ obstacle = texturePack.loadAtlas(obstacleId, 8, 8);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getPreviewId() {
+ return previewId;
+ }
+
+ public Texture getBackgroundTexture() {
+ return background;
+ }
+
+ public Texture getTerrainTexture() {
+ return terrain;
+ }
+
+ public Texture getCeilingTexture() {
+ return ceiling;
+ }
+
+ public Texture getObstacleTexture() {
+ return obstacle;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/DataSafer.java b/app/src/main/java/de/frajul/endlessroll/main/DataSafer.java
new file mode 100644
index 0000000..808a3d2
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/DataSafer.java
@@ -0,0 +1,12 @@
+package de.frajul.endlessroll.main;
+
+import de.frajul.endlessroll.user.User;
+
+/**
+ * Created by Julian on 15.07.2016.
+ */
+public class DataSafer {
+
+ private User user;
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/DataStorageHandler.java b/app/src/main/java/de/frajul/endlessroll/main/DataStorageHandler.java
new file mode 100644
index 0000000..c161367
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/DataStorageHandler.java
@@ -0,0 +1,105 @@
+package de.frajul.endlessroll.main;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import de.frajul.endlessroll.entities.shapes.PlayerShape;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.sqlDatabase.MyDatabase;
+import de.frajul.endlessroll.user.ToolSlotSettings;
+import de.frajul.endlessroll.user.User;
+
+/**
+ * Created by Julian on 25.04.2016.
+ */
+public class DataStorageHandler {
+
+ private final String PREFERENCES_NAME = "GamePreferences";
+ private final String SOUND_ON = "Sound";
+ private final String USER_EP = "EP";
+ private final String USER_LEVEL = "Level";
+ private final String USER_STARS = "Stars";
+ private final String USER_ENERGY = "Energy";
+ private final String USER_TOOL_1 = "Tool1";
+ private final String USER_TOOL_2 = "Tool2";
+ private final String USER_TOOL_3 = "Tool3";
+ private final String USER_TOOL_4 = "Tool4";
+ private final String USER_TOOLS_LOCKED = "ToolsLocked";
+ private final String USER_PLAYER_SHAPE = "PlayerShape";
+ private final String TOOL_SHOP_TUTORIAL_PART_1_FINISHED = "ToolShopTutorialPart1Finished";
+
+ private SharedPreferences preferences;
+ private MyDatabase database;
+
+ public DataStorageHandler(Activity activity) {
+ preferences = activity.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
+ database = new MyDatabase(activity);
+ }
+
+ public boolean readIsSoundOn() {
+ return preferences.getBoolean(SOUND_ON, false);
+ }
+
+ public void writeSoundOn(boolean soundOn) {
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putBoolean(SOUND_ON, soundOn);
+ editor.apply();
+ }
+
+ public User readUserData(User.LvUpListener lvUpListener) throws Exception {
+ int ep = preferences.getInt(USER_EP, 0);
+ int level = preferences.getInt(USER_LEVEL, 1);
+ int stars = preferences.getInt(USER_STARS, 0);
+ int energy = preferences.getInt(USER_ENERGY, 0);
+ int toolsLocked = preferences.getInt(USER_TOOLS_LOCKED, 3);
+ String tool1 = preferences.getString(USER_TOOL_1, ToolType.RAMP.name());
+ String tool2 = preferences.getString(USER_TOOL_2, "null");
+ String tool3 = preferences.getString(USER_TOOL_3, "null");
+ String tool4 = preferences.getString(USER_TOOL_4, "null");
+ ToolSlotSettings toolSlotSettings = new ToolSlotSettings(tool1, tool2, tool3, tool4,
+ toolsLocked);
+ String playerShapeName = preferences.getString(USER_PLAYER_SHAPE, PlayerShape.BALL.name());
+ PlayerShape playerShape;
+ try{
+ playerShape = PlayerShape.valueOf(playerShapeName);
+ }catch (Exception e){
+ playerShape = PlayerShape.BALL;
+ }
+ return new User(lvUpListener, ep, level, stars, energy, toolSlotSettings, playerShape);
+ }
+
+ public void writeUserData(User user) {
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putInt(USER_EP, user.getEp());
+ editor.putInt(USER_LEVEL, user.getLevel());
+ editor.putInt(USER_STARS, user.getStarCount());
+ editor.putInt(USER_ENERGY, user.getEnergyCount());
+ editor.putString(USER_TOOL_1, toolSlotToString(user.getToolSlotSettings(), 0));
+ editor.putString(USER_TOOL_2, toolSlotToString(user.getToolSlotSettings(), 1));
+ editor.putString(USER_TOOL_3, toolSlotToString(user.getToolSlotSettings(), 2));
+ editor.putString(USER_TOOL_4, toolSlotToString(user.getToolSlotSettings(), 3));
+ editor.putInt(USER_TOOLS_LOCKED, user.getToolSlotSettings().getLockedSlotCount());
+ editor.putString(USER_PLAYER_SHAPE, user.getCurrentPlayerShape().name());
+ editor.apply();
+ }
+
+ private String toolSlotToString(ToolSlotSettings toolSlotSettings, int index) {
+ ToolType toolType = toolSlotSettings.get(index).getToolType();
+ return toolType == null ? "null" : toolType.toString();
+ }
+
+ public void writeToolShopTutorialPart1Finished(boolean finished) {
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putBoolean(TOOL_SHOP_TUTORIAL_PART_1_FINISHED, finished);
+ editor.apply();
+ }
+
+ public boolean readToolShopTutorialPart1Finished() {
+ return preferences.getBoolean(TOOL_SHOP_TUTORIAL_PART_1_FINISHED, false);
+ }
+
+ public MyDatabase getDatabase() {
+ return database;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/ExceptionHandler.java b/app/src/main/java/de/frajul/endlessroll/main/ExceptionHandler.java
new file mode 100644
index 0000000..0aba872
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/ExceptionHandler.java
@@ -0,0 +1,10 @@
+package de.frajul.endlessroll.main;
+
+/**
+ * Created by Julian on 06.02.2016.
+ */
+public interface ExceptionHandler {
+
+ void onException(Exception e);
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/GameActivity.java b/app/src/main/java/de/frajul/endlessroll/main/GameActivity.java
new file mode 100644
index 0000000..6fd94a9
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/GameActivity.java
@@ -0,0 +1,292 @@
+package de.frajul.endlessroll.main;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.content.res.Configuration;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.RelativeLayout;
+
+import java.util.List;
+
+import de.frajul.endlessroll.entities.shapes.PlayerShape;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.levels.Level;
+import de.frajul.endlessroll.levels.LevelManager;
+import de.frajul.endlessroll.levels.LevelPack;
+import de.frajul.endlessroll.main.screens.GLTestScreen;
+import de.frajul.endlessroll.main.screens.GameScreen;
+import de.frajul.endlessroll.main.screens.LevelsScreen;
+import de.frajul.endlessroll.main.screens.PlayerShapeShopScreen;
+import de.frajul.endlessroll.main.screens.PreStartScreen;
+import de.frajul.endlessroll.main.screens.Screen;
+import de.frajul.endlessroll.main.screens.ScreenFlipper;
+import de.frajul.endlessroll.main.screens.SettingsScreen;
+import de.frajul.endlessroll.main.screens.StartScreen;
+import de.frajul.endlessroll.main.screens.ToolShopScreen;
+import de.frajul.endlessroll.main.screens.WorldsScreen;
+import de.frajul.endlessroll.main.tutorial.BreakPoint;
+import de.frajul.endlessroll.main.tutorial.TutorialManager;
+import de.frajul.endlessroll.main.tutorial.TutorialView;
+import de.frajul.endlessroll.rendering.renderer.GameRenderer;
+import de.frajul.endlessroll.sounds.SoundManager;
+import de.frajul.endlessroll.sqlDatabase.MyDatabase;
+import de.frajul.endlessroll.user.User;
+import de.frajul.endlessroll.views.GoalMessage;
+import de.frajul.endlessroll.views.LevelupMessage;
+import de.frajul.endlessroll.views.TaskCompletedMessage;
+
+/**
+ * Created by Julian on 06.02.2016.
+ */
+public class GameActivity extends Activity implements ExceptionHandler, User.LvUpListener {
+
+ private DataStorageHandler dataStorageHandler;
+ private LevelManager levelManager;
+ private SoundManager soundManager;
+ private User user;
+ private Typeface typeface;
+ private TutorialManager tutorialManager;
+
+ private MyGlSurfaceView glSurfaceView;
+ private ScreenFlipper flipper;
+ private PreStartScreen preStartScreen;
+ private StartScreen startScreen;
+ private GLTestScreen glTestScreen;
+ private WorldsScreen worldsScreen;
+ private LevelsScreen levelsScreen;
+ private ToolShopScreen toolShopScreen;
+ private PlayerShapeShopScreen shapeShopScreen;
+ private GameScreen gameScreen;
+ private SettingsScreen settingsScreen;
+
+ private LevelupMessage levelupMessage;
+ private TaskCompletedMessage taskCompletedMessage;
+ private TutorialView tutorialView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ try {
+ GameLog.d("OnCreate");
+ super.onCreate(savedInstanceState);
+ super.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ super.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ if (!hasGLES20())
+ throw new Exception("OpenGL ES 2.0 not supported");
+
+ dataStorageHandler = new DataStorageHandler(this);
+ dataStorageHandler.getDatabase().open();
+ dataStorageHandler.getDatabase().readToolData();
+ dataStorageHandler.getDatabase().close();
+
+ user = dataStorageHandler.readUserData(this);
+
+ soundManager = new SoundManager(this);
+ soundManager.setSoundOn(dataStorageHandler.readIsSoundOn());
+ soundManager.backgroundMusic.getPlayer().setLooping(true);
+ soundManager.backgroundMusic.start();
+
+ levelManager = new LevelManager(this, dataStorageHandler);
+ tutorialManager = new TutorialManager();
+
+ this.glSurfaceView = new MyGlSurfaceView(this, new GameRenderer(this));
+ typeface = Typeface.createFromAsset(getAssets(), "fontBaron.ttf");
+
+ preStartScreen = new PreStartScreen(this);
+ startScreen = new StartScreen(this, glSurfaceView);
+ glTestScreen = new GLTestScreen(this, glSurfaceView);
+ worldsScreen = new WorldsScreen(this);
+ levelsScreen = new LevelsScreen(this);
+ toolShopScreen = new ToolShopScreen(this);
+ shapeShopScreen = new PlayerShapeShopScreen(this);
+ gameScreen = new GameScreen(this, glSurfaceView);
+ settingsScreen = new SettingsScreen(this);
+
+ levelupMessage = new LevelupMessage(this);
+ taskCompletedMessage = new TaskCompletedMessage(this);
+ tutorialView = new TutorialView(this);
+
+ flipper = new ScreenFlipper(this, preStartScreen, startScreen, worldsScreen,
+ levelsScreen, gameScreen, toolShopScreen, glTestScreen, settingsScreen,
+ shapeShopScreen);
+ RelativeLayout relativeLayout = new RelativeLayout(this);
+ relativeLayout.addView(glSurfaceView);
+ relativeLayout.addView(flipper);
+ relativeLayout.addView(levelupMessage.getLayout());
+ relativeLayout.addView(taskCompletedMessage.getLayout());
+ relativeLayout.addView(tutorialView.getLayout());
+ setContentView(relativeLayout);
+ } catch (Exception e) {
+ onException(e);
+ return;
+ }
+ }
+
+ public void exitGame() {
+ super.finish();
+ }
+
+ public void flipToScreen(final Screen.ScreenType screen) {
+ if (screen != Screen.ScreenType.NONE) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ flipper.showScreen(screen, flipper.getCurrentScreen().getType());
+ }
+ });
+ }
+ }
+
+ public void onSurfaceChanged() {
+ if (flipper.getCurrentScreen().getType() == Screen.ScreenType.PRE_START)
+ flipToScreen(Screen.ScreenType.START);
+ }
+
+ public void onWorldSelected(LevelPack levelPack) {
+ levelsScreen.onLevelPackSelected(levelPack);
+ }
+
+ public void startGame(final LevelPack levelPack, final Level level) {
+ flipToScreen(Screen.ScreenType.GAME);
+ gameScreen.startGame(levelPack, level);
+ }
+
+ @Override
+ public void onLvUp(final int level) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ levelupMessage.show(level);
+ }
+ });
+ }
+
+ public void onTasksCompleted(final List shapes) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ taskCompletedMessage.show(shapes);
+ }
+ });
+ }
+
+ public void showTutorialScreen(final List breakPoints) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ tutorialView.show(breakPoints);
+ }
+ });
+ }
+
+ public void onTutorialViewHidden() {
+ if (!gameScreen.isLevelFinished())
+ gameScreen.onResume();
+ }
+
+ public void resetData() {
+ user.clearData();
+ dataStorageHandler.writeUserData(user);
+ dataStorageHandler.writeToolShopTutorialPart1Finished(false);
+ levelManager.reset();
+ tutorialManager.getToolShopTutorial().reset();
+ MyDatabase database = dataStorageHandler.getDatabase();
+ database.open();
+ database.clearLevelProgess();
+ database.clearLevelPackLocked();
+ for (ToolType type : ToolType.values())
+ type.reset();
+ database.writeToolData();
+ database.close();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ try {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (tutorialView.isShowingTutorial())
+ tutorialView.onClick(null);
+ else
+ flipper.getCurrentScreen().onBackKeyDown();
+ return true;
+ }
+ } catch (Exception e) {
+ onException(e);
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public void onException(Exception e) {
+ GameLog.e(e);
+ super.finish();
+ }
+
+ @Override
+ protected void onPause() {
+ GameLog.d("OnPause");
+ glSurfaceView.onPause();
+ if (flipper.getCurrentScreen() == gameScreen)
+ gameScreen.onPause();
+ soundManager.pause();
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ GameLog.d("OnResume");
+ glSurfaceView.onResume();
+ soundManager.resume();
+ super.onResume();
+ }
+
+ @Override
+ protected void onDestroy() {
+ GameLog.d("OnDestroy");
+ soundManager.destroy();
+
+ dataStorageHandler.writeSoundOn(soundManager.isSoundOn());
+ super.onDestroy();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+ private boolean hasGLES20() {
+ ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ ConfigurationInfo info = am.getDeviceConfigurationInfo();
+ return info.reqGlEsVersion >= 0x20000;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public DataStorageHandler getDataStorageHandler() {
+ return dataStorageHandler;
+ }
+
+ public SoundManager getSoundManager() {
+ return soundManager;
+ }
+
+ public Typeface getTypeface() {
+ return typeface;
+ }
+
+ public LevelManager getLevelManager() {
+ return levelManager;
+ }
+
+ public TutorialManager getTutorialManager() {
+ return tutorialManager;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/GameHandler.java b/app/src/main/java/de/frajul/endlessroll/main/GameHandler.java
new file mode 100644
index 0000000..bdc6881
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/GameHandler.java
@@ -0,0 +1,23 @@
+package de.frajul.endlessroll.main;
+
+import android.widget.RelativeLayout;
+
+import de.frajul.endlessroll.main.screens.Screen;
+import de.frajul.endlessroll.main.tutorial.BreakPoint;
+
+import java.util.List;
+
+/**
+ * Created by Julian on 08.12.2015.
+ */
+public interface GameHandler extends ExceptionHandler {
+
+ void startInUiThread(Runnable runnable);
+
+ void toScreen(Screen.ScreenType screen);
+
+ RelativeLayout getRootLayout();
+
+ void showTutorialScreen(List breakPoints);
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/GameLog.java b/app/src/main/java/de/frajul/endlessroll/main/GameLog.java
new file mode 100644
index 0000000..d8c76cb
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/GameLog.java
@@ -0,0 +1,44 @@
+package de.frajul.endlessroll.main;
+
+import android.util.Log;
+
+/**
+ * Created by Julian on 23.11.2015.
+ */
+public class GameLog {
+
+ private final static String TAG = "GameLog";
+ public static boolean debugging = true;
+
+ public static void i(String message) {
+ Log.i(TAG + getCallerInfo(), message);
+ }
+
+ public static void d(String message) {
+ if (debugging)
+ Log.d(TAG + getCallerInfo(), message);
+ }
+
+ public static void e(String message) {
+ Log.e(TAG + getCallerInfo(), message);
+ }
+
+ public static void e(Throwable error) {
+ Log.e(TAG + getCallerInfo(), error.getMessage(), error);
+ }
+
+ //Possible to get Method which called i, d, e
+ //Method found at stack[4]
+ public static void stack() {
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+ Log.i(TAG + "Stack", "StackSize: " + stack.length);
+ for (int i = 0; i < stack.length; i++) {
+ Log.i(TAG + "Stack", i + ": " + stack[i]);
+ }
+ }
+
+ private static String getCallerInfo() {
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+ return "(" + stack[4].getFileName() + ", " + stack[4].getMethodName() + ", " + stack[4].getLineNumber() + ")";
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/MyGlSurfaceView.java b/app/src/main/java/de/frajul/endlessroll/main/MyGlSurfaceView.java
new file mode 100644
index 0000000..1994479
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/MyGlSurfaceView.java
@@ -0,0 +1,45 @@
+package de.frajul.endlessroll.main;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.view.MotionEvent;
+import android.view.View;
+
+import de.frajul.endlessroll.rendering.Rendering;
+import de.frajul.endlessroll.rendering.renderer.GameRenderer;
+
+/**
+ * Created by Julian on 30.07.2016.
+ */
+public class MyGlSurfaceView extends GLSurfaceView {
+
+ private GameRenderer renderer;
+
+ public MyGlSurfaceView(Context context, GameRenderer gameRenderer) throws Exception {
+ super(context);
+ this.renderer = gameRenderer;
+ super.setEGLContextClientVersion(2);
+ super.setRenderer(renderer);
+ }
+
+ public void addRendering(Rendering rendering) {
+ renderer.addRendering(rendering);
+ }
+
+ public void setCurrentRendering(Rendering currentRendering) {
+ super.setOnTouchListener(currentRendering);
+ renderer.setCurrentRendering(currentRendering);
+ }
+
+ @Override
+ public void onResume() {
+ GameLog.i("GLSurfaceView: onResume");
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ GameLog.i("GLSurfaceView: onPause");
+ super.onPause();
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/game/Camera.java b/app/src/main/java/de/frajul/endlessroll/main/game/Camera.java
new file mode 100644
index 0000000..f126ace
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/game/Camera.java
@@ -0,0 +1,45 @@
+package de.frajul.endlessroll.main.game;
+
+/**
+ * Created by Julian on 22.05.2017.
+ */
+
+public class Camera {
+
+ private final float MOVE_SPEED_UP = 0.7f;
+ private final float MOVE_SPEED_DOWN = 0.7f;
+ private final float MAX_Y = 0.5f;
+ private final float MIN_Y = 0;
+ private float x, y;
+
+ public void update(float playerY, Timer timer){
+ float frameTime = timer.getFrameTimeSeconds() / 1000f;
+ float maxY = Math.min(playerY - 1 + 0.6f, MAX_Y);
+ if(playerY >= 0.5f){
+ y += MOVE_SPEED_UP * frameTime;
+ if(y > maxY)
+ y = maxY;
+ } else if(y > MIN_Y){
+ y -= MOVE_SPEED_DOWN * frameTime;
+ if(y < MIN_Y)
+ y = MIN_Y;
+ }
+ }
+
+ public void moveX(float move){
+ x += move;
+ }
+
+ public void reset(){
+ x = 0;
+ y = 0;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/game/Game.java b/app/src/main/java/de/frajul/endlessroll/main/game/Game.java
new file mode 100644
index 0000000..aa6918a
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/game/Game.java
@@ -0,0 +1,337 @@
+package de.frajul.endlessroll.main.game;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.DestroyEffect;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collectables.Energy;
+import de.frajul.endlessroll.entities.collectables.Star;
+import de.frajul.endlessroll.entities.collision.CollisionManager;
+import de.frajul.endlessroll.entities.particles.Firework;
+import de.frajul.endlessroll.entities.particles.ParticleSystem;
+import de.frajul.endlessroll.entities.shapes.PlayerShape;
+import de.frajul.endlessroll.entities.shapes.Task;
+import de.frajul.endlessroll.entities.textures.TexturePack;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.levels.Level;
+import de.frajul.endlessroll.levels.LevelPack;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.GameHandler;
+import de.frajul.endlessroll.main.GameLog;
+import de.frajul.endlessroll.main.physics.Physics;
+import de.frajul.endlessroll.main.screens.Screen;
+import de.frajul.endlessroll.main.tutorial.Tutorial;
+import de.frajul.endlessroll.rendering.Rendering;
+import de.frajul.endlessroll.sqlDatabase.MyDatabase;
+import de.frajul.endlessroll.views.ToolButton;
+import de.frajul.endlessroll.views.ToolButtonBar;
+import de.frajul.endlessroll.views.ViewManager;
+
+/**
+ * Created by Julian on 26.11.2015.
+ */
+public class Game extends Rendering {
+
+ private GameActivity gameActivity;
+ private GameHandler handler;
+ private ViewManager viewManager;
+ private LevelPack levelPack;
+ private ParticleSystem particleSystem;
+ private Firework firework;
+
+ private ToolType currentTool;
+ private Player player;
+ private Physics physics;
+ private CollisionManager collisionManager;
+ private Timer timer;
+ private GameState gameState = GameState.COUNTDOWN;
+
+ private Level level;
+ private List collectedStars = new ArrayList<>();
+ private boolean energyCollected;
+
+ private Tutorial currentTutorial;
+
+ public Game(GameHandler handler, GameActivity gameActivity) throws Exception {
+ super(gameActivity);
+ this.handler = handler;
+ this.gameActivity = gameActivity;
+ physics = new Physics();
+ collisionManager = new CollisionManager(this);
+ particleSystem = new ParticleSystem(getContext());
+ viewManager = new ViewManager(this, handler, gameActivity);
+ }
+
+ @Override
+ public GameScene init(TexturePack texturePack, Timer timer, boolean isFirstTime) {
+ GameLog.d("init Game");
+ this.timer = timer;
+ try {
+ if (isFirstTime) {
+ scene = new GameScene(texturePack, particleSystem);
+ firework = new Firework(particleSystem.firework, scene.getCamera());
+ if (level != null)
+ startGame(levelPack, level);
+ } else {
+ scene.setTexturePack(texturePack);
+ }
+ particleSystem.loadTextures();
+ } catch (Exception e) {
+ onException(e);
+ }
+ return scene;
+ }
+
+ public void startGame(LevelPack levelPack, Level level) {
+ GameLog.d("Start game");
+ try {
+ this.level = level;
+ this.levelPack = levelPack;
+ if (scene != null) {
+ gameState = GameState.COUNTDOWN;
+ gameActivity.getTutorialManager().resetGameTutorials();
+ currentTutorial = gameActivity.getTutorialManager().getGameTutorial(level);
+ if (level.isFinished())
+ currentTutorial = null;
+ collectedStars.clear();
+ energyCollected = false;
+ particleSystem.deleteAllSources();
+ scene.loadLevel(level, levelPack.getWorld(),
+ gameActivity.getUser().getCurrentPlayerShape());
+ player = scene.getPlayer();
+ viewManager.resetViews(gameActivity.getUser());
+ currentTool = viewManager.toolButtonBar.getActiveButton().getToolType();
+ viewManager.startCountdown();
+ }
+ } catch (Exception e) {
+ onException(e);
+ }
+ }
+
+ public void countdownFinished() {
+ gameState = GameState.RUNNING;
+ }
+
+ @Override
+ public void setScreenSize(int width, int height) {
+ Vector screenSize = new Vector(width, height);
+ scene.setScreenSize(screenSize);
+ }
+
+ @Override
+ public void update() {
+ try {
+ particleSystem.update(timer);
+ if (scene == null || player == null)
+ return;
+
+ float playerProgress = player.getProgress();
+ float playerSpeed = player.getMovement().getX();
+ viewManager.update(gameState == GameState.RUNNING, timer, playerProgress, playerSpeed);
+ switch (gameState) {
+ case RUNNING:
+ if (player.getPosition().y < -2f) {
+ onGameOver(false);
+ return;
+ }
+ if (player.getPosition().x >= scene.getGoalX()) {
+ onGoalReached();
+ return;
+ }
+ scene.getCamera().update(player.getPosition().y, timer);
+
+ if (currentTutorial != null) {
+ currentTutorial.update(playerProgress);
+ if (currentTutorial.isOverNewBreakPoints()) {
+ gameState = GameState.PAUSED;
+ handler.showTutorialScreen(currentTutorial.getCurrentBreakPoints());
+ return;
+ }
+ }
+
+ physics.applyGravity(scene, timer);
+ scene.update(timer);
+ collisionManager.update(physics, scene, timer);
+ break;
+ }
+ } catch (Exception e) {
+ onException(e);
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (gameState == GameState.RUNNING) {
+ ToolButtonBar bar = viewManager.toolButtonBar;
+ ToolButton button = bar.getByToolType(currentTool);
+ if (button != null && button.finishedLoading()) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ button.setProgress(0);
+ addTool(event.getX(), event.getY());
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public void resetViews() {
+ viewManager.resetViews(gameActivity.getUser());
+ }
+
+ public void continueGame() {
+ viewManager.hideShortMenu();
+ gameState = GameState.COUNTDOWN;
+ viewManager.startCountdown();
+ }
+
+ public void startNextLevel() {
+ level = levelPack.getNextLevel(level);
+ startGame(levelPack, level);
+ }
+
+ public void restartLevel() {
+ startGame(levelPack, level);
+ }
+
+ public void toLevelsScreen() {
+ handler.toScreen(Screen.ScreenType.LEVELS);
+ }
+
+ public void toToolShop() {
+ handler.toScreen(Screen.ScreenType.TOOL_SHOP);
+ }
+
+ public void setCurrentTool(ToolType toolType) {
+ currentTool = toolType;
+ }
+
+ public void tryToPause() {
+ if (gameState == GameState.GAME_OVER || gameState == GameState.LEVEL_FINISHED || gameState == GameState.PAUSED)
+ return;
+ viewManager.showShortMenu();
+ if (gameState == GameState.COUNTDOWN)
+ viewManager.stopCountdown();
+ gameState = GameState.PAUSED;
+ }
+
+ public void onGoalMessageKeyBack(){
+ viewManager.onGoalMessageKeyBack();
+ }
+
+ public void setRunning() {
+ gameState = GameState.RUNNING;
+ }
+
+ private void addTool(float x, float y) {
+ try {
+ gameActivity.getSoundManager().playSound(currentTool.getPlacingSound());
+ scene.addTool(currentTool, x, y, physics);
+ } catch (Exception e) {
+ onException(e);
+ }
+ }
+
+ public void onGameOver(boolean playerExplode) {
+ if (playerExplode) {
+ scene.getUncategorizedEntities().remove(player);
+ DestroyEffect.EXPLOSION.createEffect(particleSystem, player.getPosition(),
+ new Vector(player.getWidth(), player.getHeight())).start();
+ }
+ gameState = GameState.GAME_OVER;
+ viewManager.showGameOverMessage();
+ }
+
+ private void onGoalReached() {
+ List alreadyUnlockedShapes = new ArrayList<>();
+ for (PlayerShape shape : PlayerShape.values()) {
+ Task task = shape.getUnlockTask();
+ task.update(gameActivity.getLevelManager());
+ if (task.isConditionFulfilled())
+ alreadyUnlockedShapes.add(shape);
+ }
+
+
+ gameState = GameState.LEVEL_FINISHED;
+ if (!level.isFinished())
+ gameActivity.getUser().gainLvFinishedEp();
+ level.setFinished(true);
+
+ for (int i = 0; i <= 2; i++) {
+ if (collectedStars.contains(i)) {
+ level.setStarCollected(i, true);
+ gameActivity.getUser().onStarCollected();
+ }
+ }
+
+ if (energyCollected) {
+ level.setEnergyCollected(true);
+ gameActivity.getUser().onEnergyCollected();
+ }
+
+ firework.start();
+
+ //viewManager.showGameOverMessage(levelPack.isLastLevel(level), MessageType.WIN);
+ //TODO: fadeInWithDelay something
+ viewManager.showGoalMessage(levelPack, level);
+
+ MyDatabase database = gameActivity.getDataStorageHandler().getDatabase();
+ database.open();
+ database.writeLevelProgress(level);
+
+ List newUnlockedShapes = new ArrayList<>();
+ for (PlayerShape shape : PlayerShape.values()) {
+ Task task = shape.getUnlockTask();
+ task.update(gameActivity.getLevelManager());
+ if (task.isConditionFulfilled() && !alreadyUnlockedShapes.contains(shape))
+ newUnlockedShapes.add(shape);
+ }
+ if (!newUnlockedShapes.isEmpty())
+ gameActivity.onTasksCompleted(newUnlockedShapes);
+
+ Level nextLevel = levelPack.getNextLevel(level);
+ if (nextLevel != null) {
+ nextLevel.setLocked(false);
+ database.writeLevelProgress(nextLevel);
+ } else {
+ LevelPack nextLevelPack = gameActivity.getLevelManager().getNextLevelPack(levelPack);
+ if (nextLevelPack != null) {
+ nextLevelPack.setLocked(false);
+ database.writeLevelPackLocked(nextLevelPack);
+ }
+ }
+ database.close();
+ gameActivity.getDataStorageHandler().writeUserData(gameActivity.getUser());
+
+ if (currentTutorial != null) {
+ currentTutorial.onLevelFinished();
+ if (currentTutorial.isOverNewBreakPoints())
+ handler.showTutorialScreen(currentTutorial.getCurrentBreakPoints());
+ }
+ }
+
+ public void onStarCollision(Star star) {
+ scene.onStarCollision(star);
+ collectedStars.add(star.getIndex());
+ }
+
+ public void onEnergyCollision(Energy energy) {
+ scene.onEnergyCollision(energy);
+ energyCollected = true;
+ }
+
+ @Override
+ public void onException(Exception e) {
+ handler.onException(e);
+ }
+
+ public GameState getGameState() {
+ return gameState;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/game/GameScene.java b/app/src/main/java/de/frajul/endlessroll/main/game/GameScene.java
new file mode 100644
index 0000000..b6a6ddf
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/game/GameScene.java
@@ -0,0 +1,112 @@
+package de.frajul.endlessroll.main.game;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.DestroyEffect;
+import de.frajul.endlessroll.entities.Goal;
+import de.frajul.endlessroll.entities.Obstacle;
+import de.frajul.endlessroll.entities.collectables.Energy;
+import de.frajul.endlessroll.entities.collectables.Star;
+import de.frajul.endlessroll.entities.collision.CollisionDetector;
+import de.frajul.endlessroll.entities.particles.ParticleSystem;
+import de.frajul.endlessroll.entities.shapes.PlayerShape;
+import de.frajul.endlessroll.entities.textures.TexturePack;
+import de.frajul.endlessroll.entities.tools.Bomb;
+import de.frajul.endlessroll.entities.tools.Tool;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.levels.Level;
+import de.frajul.endlessroll.levels.ObstacleData;
+import de.frajul.endlessroll.levels.worlds.World;
+import de.frajul.endlessroll.main.GameLog;
+import de.frajul.endlessroll.main.physics.Physics;
+
+/**
+ * Created by Julian on 27.11.2015.
+ */
+public class GameScene extends Scene {
+
+ private World currentWorld;
+ private CollisionDetector collisionDetector;
+ private Goal goal;
+ private float goalX;
+
+ public GameScene(TexturePack texturePack, ParticleSystem particleSystem) throws Exception {
+ super(texturePack, particleSystem);
+ collisionDetector = new CollisionDetector();
+
+ goal = new Goal(textures.goal);
+ }
+
+ public void loadLevel(Level level, World world, PlayerShape playerPlayerShape) throws Exception {
+ this.currentWorld = world;
+ super.reset();
+ background.changeTexture(world.getBackgroundTexture());
+ terrain.loadData(world, level.getTerrainEdge(), level.getTerrainTiles());
+ ceiling.loadData(world, level.getCeilingEdge(), level.getCeilingTiles());
+ uncategorizedEntities.add(goal);
+ player.init(playerPlayerShape, terrain.getEdge(), level.getStartSpeed(), level.getEndSpeed(), particleSystem);
+ uncategorizedEntities.add(player);
+ collectables.init(level, textures);
+ for (ObstacleData data : level.getObstacles())
+ addObstacle(data);
+
+ goalX = level.getGoalX();
+ goal.setGoalX(goalX);
+ GameLog.d("Level " + level.getId() + " successfully loaded");
+ }
+
+ public void onStarCollision(Star collisionStar) {
+ collisionStar.destroy(DestroyEffect.STAR_EXPLOSION);
+ }
+
+ public void onEnergyCollision(Energy energy) {
+ energy.destroy(DestroyEffect.ENERGY_COLLECT);
+ }
+
+ public void addObstacle(ObstacleData data) {
+ Obstacle obstacle = new Obstacle(currentWorld, data, terrain.getEdge());
+ obstacles.add(obstacle);
+ }
+
+ public void addTool(ToolType type, float screenX, float screenY, Physics physics) throws Exception {
+ Vector position = calcWorldFromScreenCoords(screenX, screenY);
+ Tool tool = type.newInstance(position, particleSystem);
+ physics.checkSingleToolCollision(tool, this);
+
+ if (tool == null)
+ throw new Exception(
+ "Current ToolType(" + type + ") returns null at method newInstance()");
+ tools.add(tool);
+ }
+
+
+ public void update(Timer timer) {
+ player.setSpeedByProgress(player.getProgress() / goalX);
+ player.preMoveUpdate(timer);
+
+ if (player.hasSuperPower() && player.getBottomEdge() < terrain.getEdge()) {
+ player.setToTerrain(terrain.getEdge());
+ if (player.getMovement().y < 0)
+ player.getMovement().setY(0);
+ }
+
+ super.update(timer);
+
+ player.postMoveUpdate();
+
+ synchronized (tools) {
+ for (Tool tool : tools) {
+ if (tool instanceof Bomb) {
+ Bomb bomb = (Bomb) tool;
+ if (bomb.isExploding())
+ bomb.explode(obstacles, collisionDetector);
+ }
+ }
+ }
+
+ }
+
+ public float getGoalX() {
+ return goalX;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/game/GameState.java b/app/src/main/java/de/frajul/endlessroll/main/game/GameState.java
new file mode 100644
index 0000000..bf57f05
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/game/GameState.java
@@ -0,0 +1,10 @@
+package de.frajul.endlessroll.main.game;
+
+/**
+ * Created by Julian on 02.02.2016.
+ */
+public enum GameState {
+
+ RUNNING, PAUSED, GAME_OVER, LEVEL_FINISHED, COUNTDOWN
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/game/Scene.java b/app/src/main/java/de/frajul/endlessroll/main/game/Scene.java
new file mode 100644
index 0000000..2ee76de
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/game/Scene.java
@@ -0,0 +1,191 @@
+package de.frajul.endlessroll.main.game;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.AnimatedEntity;
+import de.frajul.endlessroll.entities.Background;
+import de.frajul.endlessroll.entities.collectables.Collectables;
+import de.frajul.endlessroll.entities.collectables.Energy;
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.entities.Obstacle;
+import de.frajul.endlessroll.entities.Player;
+import de.frajul.endlessroll.entities.collectables.Star;
+import de.frajul.endlessroll.entities.particles.ParticleSystem;
+import de.frajul.endlessroll.entities.textures.TexturePack;
+import de.frajul.endlessroll.entities.tileLists.Ceiling;
+import de.frajul.endlessroll.entities.tileLists.Terrain;
+import de.frajul.endlessroll.entities.tools.Tool;
+import de.frajul.endlessroll.levels.worlds.World;
+
+/**
+ * Created by Julian on 20.07.2016.
+ */
+public abstract class Scene {
+
+ //Not sure if needed, this why set on very high value
+ private final float MAX_Y_MOVEMENT = -1f;
+
+ protected Camera camera;
+ private Vector screenSize;
+ private Entity playerArrow;
+
+ protected ParticleSystem particleSystem;
+ protected TexturePack textures;
+ protected Background background;
+ protected Terrain terrain;
+ protected Ceiling ceiling;
+ protected Player player;
+
+ protected List uncategorizedEntities = Collections.synchronizedList(new ArrayList());
+ protected List obstacles = Collections.synchronizedList(new ArrayList());
+ protected List tools = Collections.synchronizedList(new ArrayList());
+ protected Collectables collectables = new Collectables();
+
+ public Scene(TexturePack texturePack, ParticleSystem particleSystem) {
+ this.particleSystem = particleSystem;
+ setTexturePack(texturePack);
+ camera = new Camera();
+ playerArrow = new Entity(textures.playerArrow, new Vector(0, 0.9f), .2f, .2f);
+ background = new Background(World.GRASSLANDS.getBackgroundTexture());
+ terrain = new Terrain(World.GRASSLANDS.getTerrainTexture());
+ ceiling = new Ceiling(World.GRASSLANDS.getTerrainTexture());
+ player = new Player();
+ }
+
+ public void setTexturePack(TexturePack texturePack) {
+ this.textures = texturePack;
+ }
+
+ public void reset(){
+ uncategorizedEntities.clear();
+ obstacles.clear();
+ tools.clear();
+ collectables.reset();
+ camera.reset();
+ background.resetPosition();
+ }
+
+ public void update(Timer timer) {
+ updateEntityList(uncategorizedEntities, timer);
+ updateEntityList(obstacles, timer);
+ updateEntityList(tools, timer);
+ updateEntityList(collectables, timer);
+
+ if (player.getPosition().y >= player.RADIUS + 1 + camera.getY()) {
+ playerArrow.getPosition().x = player.getPosition().x;
+ playerArrow.getPosition().y = camera.getY() + 0.9f;
+ if (!uncategorizedEntities.contains(playerArrow)) {
+ uncategorizedEntities.add(playerArrow);
+ }
+ } else
+ uncategorizedEntities.remove(playerArrow);
+ }
+
+ private void updateEntityList(List extends Entity> list, Timer timer){
+ synchronized (list){
+ Iterator extends Entity> iterator = list.iterator();
+ while(iterator.hasNext()) {
+ Entity entity = iterator.next();
+ if(entity instanceof Obstacle){
+ Obstacle obstacle = (Obstacle) entity;
+ if (obstacle.isMoving())
+ obstacle.moveWithMoveComponent(timer.getFrameTimeSeconds());
+ }
+ boolean remove = updateEntity(entity, timer);
+ if (remove)
+ iterator.remove();
+ }
+ }
+ }
+
+ private boolean updateEntity(Entity entity, Timer timer){
+ if (entity instanceof AnimatedEntity)
+ ((AnimatedEntity) entity).update(timer);
+ Vector movement = entity.getMovement();
+ Vector finalMovement = new Vector(movement).mul(timer.getFrameTimeSeconds());
+ if(finalMovement.y < MAX_Y_MOVEMENT)
+ finalMovement.y = MAX_Y_MOVEMENT;
+ entity.move(finalMovement);
+ if (entity.equals(player))
+ moveEnviroment(finalMovement.x);
+
+ if (entity.isDestroyed() && entity.getDestroyEffect() != null)
+ entity.getDestroyEffect()
+ .createEffect(particleSystem, new Vector(entity.getPosition()),
+ new Vector(entity.getWidth(), entity.getHeight())).start();
+ if (entity.getRightEdge() - camera.getX() < -3f || entity.isDestroyed()) {
+ return true;
+ }
+ return false;
+ }
+
+ private void moveEnviroment(float x) {
+ camera.moveX(x);
+ background.move(x * 0.95f, camera.getX());
+ terrain.update(camera.getX());
+ ceiling.update(camera.getX());
+ }
+
+ protected Vector calcWorldFromScreenCoords(float screenX, float screenY) throws Exception {
+ if (screenSize == null)
+ throw new Exception("ScreenSize not set");
+ float glCoordWidth = (2f * screenSize.x / screenSize.y);
+ float x = ((screenX / screenSize.x) * 2f - 1f) * glCoordWidth / 2;
+ x += camera.getX();
+ float y = -((screenY / screenSize.y) * 2f - 1f);
+ y += camera.getY();
+ return new Vector(x, y);
+ }
+
+ public void setScreenSize(Vector screenSize) {
+ this.screenSize = screenSize;
+ }
+
+ public synchronized Background getBackground() {
+ return background;
+ }
+
+ public synchronized Terrain getTerrain() {
+ return terrain;
+ }
+
+ public synchronized Ceiling getCeiling() {
+ return ceiling;
+ }
+
+ public synchronized List getUncategorizedEntities() {
+ return uncategorizedEntities;
+ }
+
+ public synchronized List getObstacles() {
+ return obstacles;
+ }
+
+ public synchronized List getTools() {
+ return tools;
+ }
+
+ public synchronized Collectables getCollectables() {
+ return collectables;
+ }
+
+ public Player getPlayer() {
+ return player;
+ }
+
+ public TexturePack getTextures() {
+ return textures;
+ }
+
+ public ParticleSystem getParticleSystem() {
+ return particleSystem;
+ }
+
+ public Camera getCamera() {
+ return camera;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/game/StartScene.java b/app/src/main/java/de/frajul/endlessroll/main/game/StartScene.java
new file mode 100644
index 0000000..bf55be8
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/game/StartScene.java
@@ -0,0 +1,17 @@
+package de.frajul.endlessroll.main.game;
+
+import de.frajul.endlessroll.entities.particles.ParticleSystem;
+import de.frajul.endlessroll.entities.shapes.PlayerShape;
+import de.frajul.endlessroll.entities.textures.TexturePack;
+import de.frajul.endlessroll.levels.worlds.World;
+
+public class StartScene extends Scene {
+
+ public StartScene(TexturePack texturePack, ParticleSystem particleSystem) {
+ super(texturePack, particleSystem);
+ terrain.createEndless(World.ICY_MOUNTAINS, -.8f);
+ player.init(PlayerShape.BALL, terrain.getEdge(), 0.5f, 0.5f, null);
+ uncategorizedEntities.add(player);
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/main/game/Timer.java b/app/src/main/java/de/frajul/endlessroll/main/game/Timer.java
new file mode 100644
index 0000000..35aece4
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/game/Timer.java
@@ -0,0 +1,46 @@
+package de.frajul.endlessroll.main.game;
+
+/**
+ * Created by Julian on 22.11.2015.
+ */
+public class Timer {
+
+ private long lastFpsTime;
+ private int fpsCounter;
+ private int fps;
+
+ private long lastTime;
+ private long delta;
+
+
+ public Timer() {
+ lastTime = System.currentTimeMillis();
+ lastFpsTime = lastTime;
+ }
+
+ public void update() {
+ long currentTime = System.currentTimeMillis();
+ delta = currentTime - lastTime;
+ lastTime = currentTime;
+
+ fpsCounter++;
+ if (currentTime - lastFpsTime > 1000) {
+ fps = fpsCounter;
+ lastFpsTime += 1000;
+ fpsCounter = 0;
+ }
+ }
+
+ public float getFrameTimeSeconds() {
+ return delta;
+ }
+
+ public int getFps() {
+ return fps;
+ }
+
+ public long getCurrentTime() {
+ return System.currentTimeMillis();
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/physics/Physics.java b/app/src/main/java/de/frajul/endlessroll/main/physics/Physics.java
new file mode 100644
index 0000000..ddc032c
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/physics/Physics.java
@@ -0,0 +1,199 @@
+package de.frajul.endlessroll.main.physics;
+
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.entities.Obstacle;
+import de.frajul.endlessroll.entities.collectables.Collectables;
+import de.frajul.endlessroll.entities.collectables.Energy;
+import de.frajul.endlessroll.entities.collectables.Star;
+import de.frajul.endlessroll.entities.collision.CollisionDetector;
+import de.frajul.endlessroll.entities.collision.collisionData.EntityCollisionData;
+import de.frajul.endlessroll.entities.collision.collisionData.ObstacleCollisionData;
+import de.frajul.endlessroll.entities.collision.collisionData.PlayerCollisionData;
+import de.frajul.endlessroll.entities.collision.collisionData.ToolCollisionData;
+import de.frajul.endlessroll.entities.collision.geometry.Circle;
+import de.frajul.endlessroll.entities.tileLists.Ceiling;
+import de.frajul.endlessroll.entities.tileLists.Terrain;
+import de.frajul.endlessroll.entities.tileLists.Tile;
+import de.frajul.endlessroll.entities.tools.Bomb;
+import de.frajul.endlessroll.entities.tools.Tool;
+import de.frajul.endlessroll.main.game.GameScene;
+import de.frajul.endlessroll.main.game.Scene;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 27.11.2015.
+ */
+public class Physics {
+
+ public final float GRAVITY_FORCE = .0000025f;
+ private final float TOOL_ON_OBSTACLE_LEFT_EDGE_TOLERANCE = 0.05f;
+ private CollisionDetector detector;
+
+ public Physics() {
+ detector = new CollisionDetector();
+ }
+
+ public void applyGravity(GameScene scene, Timer timer) {
+ float gravity = GRAVITY_FORCE * timer.getFrameTimeSeconds();
+
+ scene.getPlayer().setGravityForce(-gravity);
+
+ synchronized (scene.getTools()) {
+ for (Tool tool : scene.getTools()) {
+ if (tool.isFloating())
+ continue;
+ tool.getMovement().y -= gravity * 2;
+ }
+ }
+ }
+
+ public synchronized void checkToolCollision(Scene scene) {
+ synchronized (scene.getTools()) {
+ for (Tool tool : scene.getTools()) {
+ checkSingleToolCollision(tool, scene);
+ }
+ }
+ }
+
+ public synchronized void checkSingleToolCollision(Tool tool, Scene scene) {
+ if(tool.isFloating())
+ return;
+ float terrainEdge = getTerrainEdge(tool, scene.getTerrain());
+ Obstacle toolIsCollidingWith = getHighestObstacleToolIsCollidingWith(tool, scene);
+
+ if (toolIsCollidingWith != null) {
+ float distObstTool = tool.getRightEdge() - toolIsCollidingWith.getLeftEdge();
+ if (distObstTool < TOOL_ON_OBSTACLE_LEFT_EDGE_TOLERANCE) {
+ tool.getPosition().x -= TOOL_ON_OBSTACLE_LEFT_EDGE_TOLERANCE;
+ terrainEdge = getTerrainEdge(tool, scene.getTerrain());
+ toolIsCollidingWith = getHighestObstacleToolIsCollidingWith(tool, scene);
+ }
+ }
+
+ float orientingHeight = terrainEdge;
+ if (toolIsCollidingWith != null)
+ orientingHeight = Math.max(toolIsCollidingWith.getTopEdge(), terrainEdge);
+
+ if (tool.getBottomEdge() <= orientingHeight) {
+ tool.getMovement().y = 0;
+ if (tool instanceof Bomb)
+ tool.setFloating(true);
+ else
+ tool.setToTerrain(orientingHeight);
+ }
+ }
+
+ @Nullable
+ private Obstacle getHighestObstacleToolIsCollidingWith(Tool tool, Scene scene) {
+ List collisionObstacles = new ArrayList<>();
+ synchronized (scene.getObstacles()) {
+ for (Obstacle obstacle : scene.getObstacles()) {
+ if (tool.getWorldCollisionBounds().isCollisionWithQuad(obstacle, detector)) {
+ collisionObstacles.add(obstacle);
+ }
+ }
+ }
+ Obstacle highest = null;
+ for (Obstacle obstacle : collisionObstacles)
+ if (highest == null || highest.getTopEdge() < obstacle.getTopEdge())
+ highest = obstacle;
+ return highest;
+ }
+
+ private float getTerrainEdge(Entity tool, Terrain terrain) {
+ for (Tile instance : terrain) {
+ if ((tool.getLeftEdge() >= instance.getLeftEdge() && tool.getLeftEdge() <= instance
+ .getRightEdge()) || (tool.getRightEdge() <= instance.getRightEdge() && tool
+ .getRightEdge() >= instance.getLeftEdge()) || (instance.getLeftEdge() >= tool
+ .getLeftEdge() && instance.getLeftEdge() <= tool.getRightEdge()) || (instance
+ .getRightEdge() <= tool.getRightEdge() && instance.getRightEdge() >= tool
+ .getLeftEdge()))
+ return terrain.getEdge();
+ }
+ return -10;
+ }
+
+ public PlayerCollisionData getPlayerCollisionData(GameScene scene) {
+ EntityCollisionData terrainData = playerCollidesWithTerrain(scene);
+ EntityCollisionData ceilingData = playerCollidesWithCeiling(scene);
+ ObstacleCollisionData obstacleData = playerCollidesWithObstacle(scene);
+ ToolCollisionData toolData = playerCollidesWithTool(scene);
+ EntityCollisionData starData = playerCollidesWithStar(scene);
+ EntityCollisionData energyData = playerCollidesWithEnergy(scene);
+ return new PlayerCollisionData(terrainData, ceilingData, obstacleData, toolData, starData,
+ energyData);
+ }
+
+ private EntityCollisionData playerCollidesWithTerrain(GameScene scene) {
+ Terrain terrain = scene.getTerrain();
+ for (Tile terrainTile : terrain) {
+ EntityCollisionData data = detector
+ .playerEntityCollision(scene.getPlayer(), terrainTile);
+ if (data.isCollision())
+ return data;
+ }
+ return new EntityCollisionData(null, null);
+ }
+
+ private EntityCollisionData playerCollidesWithCeiling(GameScene scene) {
+ Ceiling ceiling = scene.getCeiling();
+ for (Tile ceilingTile : ceiling) {
+ EntityCollisionData data = detector
+ .playerEntityCollision(scene.getPlayer(), ceilingTile);
+ if (data.isCollision())
+ return data;
+ }
+ return new EntityCollisionData(null, null);
+ }
+
+ private ObstacleCollisionData playerCollidesWithObstacle(GameScene scene) {
+ List collisions = new ArrayList<>();
+ synchronized (scene.getObstacles()) {
+ for (Obstacle obstacle : scene.getObstacles()) {
+ EntityCollisionData data = detector
+ .playerEntityCollision(scene.getPlayer(), obstacle);
+ if (data.isCollision())
+ collisions.add(data);
+ }
+ }
+ return new ObstacleCollisionData(collisions);
+ }
+
+ private ToolCollisionData playerCollidesWithTool(GameScene scene) {
+ List tools = new ArrayList<>();
+ Circle circle = new Circle(scene.getPlayer());
+ synchronized (scene.getTools()) {
+ for (Tool tool : scene.getTools()) {
+ if (tool.getPlayerCollisionBounds().isCollisionWithCircle(circle, detector))
+ tools.add(tool);
+ }
+ }
+ return new ToolCollisionData(tools);
+ }
+
+ private EntityCollisionData playerCollidesWithStar(GameScene scene) {
+ Collectables collectables = scene.getCollectables();
+ synchronized (collectables.getStars()) {
+ for (Star star : collectables.getStars()) {
+ EntityCollisionData data = detector.playerEntityCollision(scene.getPlayer(), star);
+ if (data.isCollision())
+ return data;
+ }
+ }
+ return new EntityCollisionData(null, null);
+ }
+
+ private EntityCollisionData playerCollidesWithEnergy(GameScene scene) {
+ Energy energy = scene.getCollectables().getEnergy();
+ if (energy != null) {
+ EntityCollisionData data = detector.playerEntityCollision(scene.getPlayer(), energy);
+ return data;
+ } else
+ return new EntityCollisionData(null, null);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/screens/GLScreen.java b/app/src/main/java/de/frajul/endlessroll/main/screens/GLScreen.java
new file mode 100644
index 0000000..2045e45
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/screens/GLScreen.java
@@ -0,0 +1,21 @@
+package de.frajul.endlessroll.main.screens;
+
+import android.support.annotation.LayoutRes;
+import android.view.ViewGroup;
+
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.MyGlSurfaceView;
+
+/**
+ * Created by Julian on 30.07.2016.
+ */
+public abstract class GLScreen extends Screen {
+
+ protected MyGlSurfaceView glView;
+
+ public GLScreen(ScreenType type, GameActivity gameActivity, @LayoutRes int layoutId, MyGlSurfaceView glView) {
+ super(type, gameActivity, layoutId);
+ this.glView = glView;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/screens/GameScreen.java b/app/src/main/java/de/frajul/endlessroll/main/screens/GameScreen.java
new file mode 100644
index 0000000..fc834ad
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/screens/GameScreen.java
@@ -0,0 +1,88 @@
+package de.frajul.endlessroll.main.screens;
+
+import android.widget.RelativeLayout;
+
+import java.util.List;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.levels.Level;
+import de.frajul.endlessroll.levels.LevelPack;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.GameHandler;
+import de.frajul.endlessroll.main.MyGlSurfaceView;
+import de.frajul.endlessroll.main.game.Game;
+import de.frajul.endlessroll.main.game.GameState;
+import de.frajul.endlessroll.main.tutorial.BreakPoint;
+
+/**
+ * Created by Julian on 08.02.2016.
+ */
+public class GameScreen extends GLScreen {
+
+ private Game game;
+
+ public GameScreen(GameActivity gameActivity, MyGlSurfaceView glSurfaceView) throws Exception {
+ super(ScreenType.GAME, gameActivity, R.layout.game, glSurfaceView);
+ game = new Game(gameViewHandler, gameActivity);
+ glView.addRendering(game);
+ }
+
+ @Override
+ public void prepareToBeShown() {
+ glView.setCurrentRendering(game);
+ }
+
+ public void onPause() {
+ game.tryToPause();
+ }
+
+ public void onResume() {
+ game.setRunning();
+ }
+
+ public boolean isLevelFinished() {
+ return game.getGameState() == GameState.LEVEL_FINISHED;
+ }
+
+ @Override
+ public void onBackKeyDown() {
+ game.tryToPause();
+ if(isLevelFinished())
+ game.onGoalMessageKeyBack();
+ }
+
+ public void startGame(LevelPack levelPack, Level level) {
+ game.resetViews();
+ game.startGame(levelPack, level);
+ }
+
+ private GameHandler gameViewHandler = new GameHandler() {
+
+ @Override
+ public void startInUiThread(Runnable runnable) {
+ gameActivity.runOnUiThread(runnable);
+ }
+
+ @Override
+ public void toScreen(ScreenType screen) {
+ glView.setCurrentRendering(null);
+ gameActivity.flipToScreen(screen);
+ }
+
+ @Override
+ public RelativeLayout getRootLayout() {
+ return layout;
+ }
+
+ @Override
+ public void showTutorialScreen(List breakPoints) {
+ gameActivity.showTutorialScreen(breakPoints);
+ }
+
+ @Override
+ public void onException(Exception e) {
+ gameActivity.onException(e);
+ }
+ };
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/screens/LevelsScreen.java b/app/src/main/java/de/frajul/endlessroll/main/screens/LevelsScreen.java
new file mode 100644
index 0000000..f850e05
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/screens/LevelsScreen.java
@@ -0,0 +1,80 @@
+package de.frajul.endlessroll.main.screens;
+
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.levels.Level;
+import de.frajul.endlessroll.levels.LevelPack;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.views.LevelButton;
+import de.frajul.endlessroll.views.LevelButtonOnClickListener;
+import de.frajul.endlessroll.views.TopBar;
+
+/**
+ * Created by Julian on 23.04.2016.
+ */
+public class LevelsScreen extends Screen implements LevelButtonOnClickListener {
+
+ private LevelPack levelPack;
+
+ private TopBar topBar;
+ private LinearLayout topRow;
+ private LinearLayout bottomRow;
+
+ public LevelsScreen(GameActivity gameActivity) {
+ super(ScreenType.LEVELS, gameActivity, R.layout.levels);
+ topBar = super.createTopBar(R.id.levels_topbar);
+ topRow = (LinearLayout) layout.findViewById(R.id.levels_top_row);
+ bottomRow = (LinearLayout) layout.findViewById(R.id.levels_bottom_row);
+ }
+
+ public void onLevelPackSelected(LevelPack levelPack) {
+ this.levelPack = levelPack;
+ }
+
+ private void build() {
+ topRow.removeAllViews();
+ bottomRow.removeAllViews();
+ int levelCount = levelPack.getLevels().size();
+
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ params.setMargins(15, 15, 15, 15);
+
+ for (Level level : levelPack.getLevels())
+ createButton(level, levelCount, params);
+ }
+
+ private void createButton(Level level, int levelCount, LinearLayout.LayoutParams params) {
+ LevelButton button = new LevelButton(gameActivity, this, R.layout.levelbutton);
+ button.init(level);
+
+ int halfLevelCount = levelCount / 2;
+ if (levelCount % 2 == 1)
+ halfLevelCount++;
+
+ if (level.getId() <= halfLevelCount)
+ topRow.addView(button.getView(), params);
+ else
+ bottomRow.addView(button.getView(), params);
+ }
+
+ @Override
+ public void prepareToBeShown() {
+ topBar.update();
+ build();
+ }
+
+ @Override
+ public void onBackKeyDown() {
+ flipTo(ScreenType.WORLDS);
+ }
+
+ @Override
+ public void onClick(LevelButton levelButton) {
+ Level level = levelButton.getLevel();
+ if (!level.isLocked())
+ gameActivity.startGame(levelPack, level);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/screens/Screen.java b/app/src/main/java/de/frajul/endlessroll/main/screens/Screen.java
new file mode 100644
index 0000000..fa8ac63
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/screens/Screen.java
@@ -0,0 +1,85 @@
+package de.frajul.endlessroll.main.screens;
+
+import android.content.Context;
+import android.support.annotation.IdRes;
+import android.support.annotation.LayoutRes;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.views.TopBar;
+
+/**
+ * Created by Julian on 13.02.2016.
+ */
+public abstract class Screen {
+
+ public enum ScreenType {
+ NONE(-1),
+ PRE_START(0),
+ START(1),
+ WORLDS(2),
+ LEVELS(3),
+ GAME(4),
+ TOOL_SHOP(5),
+ GL_TEST(6),
+ SETTINGS(7),
+ SHAPE_SHOP(8);
+
+ private int inFlipperPosition;
+
+ ScreenType(int inFlipperPosition) {
+ this.inFlipperPosition = inFlipperPosition;
+ }
+
+ public int getInFlipperPosition() {
+ return inFlipperPosition;
+ }
+ }
+
+ private ScreenType type;
+ protected ScreenType caller;
+ protected V layout;
+ protected GameActivity gameActivity;
+
+ public Screen(ScreenType type, GameActivity gameActivity, @LayoutRes int layoutId) {
+ this.type = type;
+ this.gameActivity = gameActivity;
+ layout = inflateLayout(gameActivity, layoutId);
+ }
+
+ private V inflateLayout(Context context, @LayoutRes int layoutId) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ return (V) inflater.inflate(layoutId, null);
+ }
+
+ protected TopBar createTopBar(@IdRes int id) {
+ return new TopBar(gameActivity, type, layout.findViewById(id));
+ }
+
+ public void setCaller(ScreenType caller) {
+ this.caller = caller;
+ }
+
+ public abstract void prepareToBeShown();
+
+ public abstract void onBackKeyDown();
+
+ protected void flipToCaller() {
+ if (caller != null)
+ gameActivity.flipToScreen(caller);
+ }
+
+ protected void flipTo(ScreenType type) {
+ gameActivity.flipToScreen(type);
+ }
+
+ public ScreenType getType() {
+ return type;
+ }
+
+ public V get() {
+ return layout;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/screens/ScreenFlipper.java b/app/src/main/java/de/frajul/endlessroll/main/screens/ScreenFlipper.java
new file mode 100644
index 0000000..63934ec
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/screens/ScreenFlipper.java
@@ -0,0 +1,54 @@
+package de.frajul.endlessroll.main.screens;
+
+import android.content.Context;
+import android.widget.ViewFlipper;
+
+/**
+ * Created by Julian on 13.02.2016.
+ */
+public class ScreenFlipper extends ViewFlipper {
+
+ private Screen[] screens;
+ private Screen currentScreen;
+
+ public ScreenFlipper(Context context, Screen... screens) {
+ super(context);
+ this.screens = screens;
+ for (Screen screen : screens)
+ addView(screen);
+ currentScreen = screens[0];
+ showScreen(currentScreen.getType(), Screen.ScreenType.NONE);
+ }
+
+ private void addView(Screen screen) {
+ super.addView(screen.get(), screen.getType().getInFlipperPosition());
+ }
+
+ public Screen getCurrentScreen() {
+ return currentScreen;
+ }
+
+ public void showScreen(Screen.ScreenType type, Screen.ScreenType caller) {
+ Screen screen = findScreen(type);
+ screen.setCaller(caller);
+ screen.prepareToBeShown();
+
+ int positionDifference = type.getInFlipperPosition() - currentScreen.getType().getInFlipperPosition();
+ if (positionDifference < 0)
+ for (; positionDifference != 0; positionDifference++)
+ super.showPrevious();
+ else if (positionDifference > 0)
+ for (; positionDifference != 0; positionDifference--)
+ super.showNext();
+ currentScreen = screen;
+ }
+
+ private Screen findScreen(Screen.ScreenType type) {
+ for (Screen screen : screens)
+ if (screen.getType().equals(type)) {
+ return screen;
+ }
+ return null;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/screens/StartScreen.java b/app/src/main/java/de/frajul/endlessroll/main/screens/StartScreen.java
new file mode 100644
index 0000000..238e696
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/screens/StartScreen.java
@@ -0,0 +1,87 @@
+package de.frajul.endlessroll.main.screens;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.graphics.Typeface;
+import android.support.annotation.IdRes;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+import android.widget.ToggleButton;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.MyGlSurfaceView;
+import de.frajul.endlessroll.rendering.Rendering;
+import de.frajul.endlessroll.views.ExitConfirmDialog;
+
+/**
+ * Created by Julian on 07.07.2016.
+ */
+public class StartScreen extends GLScreen implements View.OnClickListener {
+
+ private Rendering rendering;
+
+ private Button play;
+ private Button unlockLevels;
+ private Button gain90EP;
+ private Button toGlTestScreen;
+ private Button settings;
+
+ private ExitConfirmDialog exitConfirmDialog;
+
+ public StartScreen(GameActivity gameActivity, MyGlSurfaceView glSurfaceView) throws Exception {
+ super(ScreenType.START, gameActivity, R.layout.start_screen, glSurfaceView);
+
+ Typeface typeface = gameActivity.getTypeface();
+ play = createButton(R.id.startscreen_play, typeface);
+ play.startAnimation(AnimationUtils.loadAnimation(gameActivity, R.anim.rotate));
+ unlockLevels = createButton(R.id.startscreen_unlock_levels, typeface);
+ gain90EP = createButton(R.id.startscreen_gain_90_ep, typeface);
+ toGlTestScreen = createButton(R.id.startscreen_to_gl_test_screen, typeface);
+ settings = (Button) layout.findViewById(R.id.startscreen_settings);
+ settings.setOnClickListener(this);
+
+ exitConfirmDialog = new ExitConfirmDialog(gameActivity);
+
+ rendering = new StartScreenRendering(gameActivity);
+ glView.addRendering(rendering);
+ }
+
+ private Button createButton(@IdRes int id, Typeface typeface) {
+ Button button = (Button) layout.findViewById(id);
+ button.setTypeface(typeface);
+ button.setOnClickListener(this);
+ return button;
+ }
+
+ @Override
+ public void prepareToBeShown() {
+ glView.setCurrentRendering(rendering);
+ }
+
+ @Override
+ public void onBackKeyDown() {
+ exitConfirmDialog.show();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.equals(play)) {
+ gameActivity.flipToScreen(ScreenType.WORLDS);
+ glView.setCurrentRendering(null);
+ } else if (v.equals(settings)) {
+ gameActivity.flipToScreen(ScreenType.SETTINGS);
+ glView.setCurrentRendering(null);
+ } else if (v.equals(gain90EP)) {
+ gameActivity.getUser().gainEp(90);
+ } else if (v.equals(unlockLevels)) {
+ gameActivity.getLevelManager().unlockAllLevels();
+ gameActivity.getLevelManager().unlockAllPacks();
+ } else if (v.equals(toGlTestScreen))
+ gameActivity.flipToScreen(ScreenType.GL_TEST);
+ }
+
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/screens/StartScreenRendering.java b/app/src/main/java/de/frajul/endlessroll/main/screens/StartScreenRendering.java
new file mode 100644
index 0000000..d57a0c2
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/screens/StartScreenRendering.java
@@ -0,0 +1,53 @@
+package de.frajul.endlessroll.main.screens;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import de.frajul.endlessroll.entities.particles.ParticleSystem;
+import de.frajul.endlessroll.entities.textures.TexturePack;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.GameLog;
+import de.frajul.endlessroll.main.game.StartScene;
+import de.frajul.endlessroll.main.game.Timer;
+import de.frajul.endlessroll.rendering.Rendering;
+
+/**
+ * Created by Julian on 20.07.2016.
+ */
+public class StartScreenRendering extends Rendering {
+
+ private Timer timer;
+ private ParticleSystem particleSystem;
+
+ public StartScreenRendering(GameActivity gameActivity) throws Exception {
+ super(gameActivity);
+ this.particleSystem = new ParticleSystem(gameActivity);
+ }
+
+ @Override
+ public StartScene init(TexturePack texturePack, Timer timer, boolean isFirstTime) {
+ GameLog.d("init Start Screen Rendering");
+ this.timer = timer;
+ if (isFirstTime)
+ scene = new StartScene(texturePack, particleSystem);
+ else
+ scene.setTexturePack(texturePack);
+ try {
+ particleSystem.loadTextures();
+ } catch (Exception e) {
+ }
+ return scene;
+ }
+
+ @Override
+ public void update() {
+ particleSystem.update(timer);
+ scene.update(timer);
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return false;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/screens/ToolShopScreen.java b/app/src/main/java/de/frajul/endlessroll/main/screens/ToolShopScreen.java
new file mode 100644
index 0000000..8d39af0
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/screens/ToolShopScreen.java
@@ -0,0 +1,171 @@
+package de.frajul.endlessroll.main.screens;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.entities.tools.ToolSlot;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.main.DataStorageHandler;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.tutorial.ToolShopTutorial;
+import de.frajul.endlessroll.user.LevelUpBounties;
+import de.frajul.endlessroll.user.ToolSlotSettings;
+import de.frajul.endlessroll.views.ToolInspector;
+import de.frajul.endlessroll.views.ToolOfferSlot;
+import de.frajul.endlessroll.views.TopBar;
+
+/**
+ * Created by Julian on 08.07.2016.
+ */
+public class ToolShopScreen extends Screen implements View.OnClickListener {
+
+ private LevelUpBounties levelUpBounties;
+ private ToolSlotSettings slotSettings;
+ private ToolOfferSlot selectedToolOfferSlot;
+
+ private TopBar topBar;
+ private LinearLayout toolOfferTopRow;
+ private LinearLayout toolOfferBottomRow;
+ private List toolSlotViews = new ArrayList<>();
+ private List toolOfferSlots = new ArrayList<>();
+
+ private ToolInspector toolInspector;
+ private ToolShopTutorial tutorial;
+
+ public ToolShopScreen(GameActivity gameActivity) {
+ super(ScreenType.TOOL_SHOP, gameActivity, R.layout.toolshop);
+ this.levelUpBounties = new LevelUpBounties(0);
+ this.slotSettings = gameActivity.getUser().getToolSlotSettings();
+ this.tutorial = gameActivity.getTutorialManager().getToolShopTutorial();
+ tutorial.setFirstPartShown(
+ gameActivity.getDataStorageHandler().readToolShopTutorialPart1Finished());
+
+ topBar = super.createTopBar(R.id.toolshop_topbar);
+ toolSlotViews.add(getToolSlotView(R.id.toolshop_slot1));
+ toolSlotViews.add(getToolSlotView(R.id.toolshop_slot2));
+ toolSlotViews.add(getToolSlotView(R.id.toolshop_slot3));
+ toolSlotViews.add(getToolSlotView(R.id.toolshop_slot4));
+ toolOfferTopRow = (LinearLayout) layout.findViewById(R.id.toolshop_tool_offer_top_row);
+ toolOfferBottomRow = (LinearLayout) layout
+ .findViewById(R.id.toolshop_tool_offer_bottom_row);
+
+ toolInspector = new ToolInspector(this, gameActivity,
+ layout.findViewById(R.id.toolshop_toolinspector));
+
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ params.setMargins(10, 0, 10, 0);
+
+ int i = 0;
+ for (ToolType type : ToolType.values()) {
+ i++;
+ ToolOfferSlot slot = new ToolOfferSlot(this, gameActivity, gameActivity.getTypeface(),
+ type);
+ toolOfferSlots.add(slot);
+ if (i > 5)
+ toolOfferBottomRow.addView(slot.getLayout(), params);
+ else
+ toolOfferTopRow.addView(slot.getLayout(), params);
+ }
+ }
+
+ private ImageView getToolSlotView(int id) {
+ FrameLayout slotLayout = (FrameLayout) layout.findViewById(id);
+ ImageView imageView = (ImageView) slotLayout.findViewById(R.id.toolslot_image);
+ imageView.setOnClickListener(this);
+ return imageView;
+ }
+
+ @Override
+ public void prepareToBeShown() {
+ topBar.update();
+ levelUpBounties.loadAllForLevel(gameActivity.getUser().getLevel());
+ slotSettings.unlockSlotsIfLevelReached(levelUpBounties);
+ onToolOfferSlotSelected(toolOfferSlots.get(0));
+
+ for (int i = 0; i < toolSlotViews.size(); i++) {
+ ToolSlot toolSlot = slotSettings.get(i);
+ ImageView toolSlotView = toolSlotViews.get(i);
+ toolSlotView.setImageResource(toolSlot.getDrawable());
+ }
+ for (ToolOfferSlot toolOfferSlot : toolOfferSlots) {
+ boolean locked = levelUpBounties.isToolLocked(toolOfferSlot.getToolType());
+ toolOfferSlot.setLocked(locked);
+ toolOfferSlot.updateBackgroundColor();
+ }
+ tutorial.onToolShopPrepare(!levelUpBounties.isToolLocked(ToolType.SPRING));
+ if (tutorial.isOverNewBreakPoints()) {
+ gameActivity.showTutorialScreen(tutorial.getCurrentBreakPoints());
+ gameActivity.getDataStorageHandler()
+ .writeToolShopTutorialPart1Finished(tutorial.isFirstPartShown());
+ }
+ }
+
+ public void onToolBought(int price, ToolType toolType) {
+ topBar.showStarcountDecrease(-price);
+ gameActivity.getUser().increaseStarCount(-price, false);
+ topBar.update();
+ DataStorageHandler dataStorageHandler = gameActivity.getDataStorageHandler();
+ dataStorageHandler.getDatabase().open();
+ dataStorageHandler.getDatabase().writeToolData();
+ dataStorageHandler.getDatabase().close();
+ for (ToolOfferSlot toolOfferSlot : toolOfferSlots) {
+ toolOfferSlot.updateBackgroundColor();
+ }
+ tutorial.onToolBought(toolType);
+ if (tutorial.isOverNewBreakPoints())
+ gameActivity.showTutorialScreen(tutorial.getCurrentBreakPoints());
+ }
+
+ public void onToolUpgraded(int price) {
+ topBar.showEnergycountDecrease(-price);
+ gameActivity.getUser().increaseEnergyCount(-price, false);
+ topBar.update();
+ DataStorageHandler dataStorageHandler = gameActivity.getDataStorageHandler();
+ dataStorageHandler.getDatabase().open();
+ dataStorageHandler.getDatabase().writeToolData();
+ dataStorageHandler.getDatabase().close();
+ }
+
+
+ public void onToolOfferSlotSelected(ToolOfferSlot slot) {
+ selectedToolOfferSlot = slot;
+ for (ToolOfferSlot toolOfferSlot : toolOfferSlots)
+ toolOfferSlot.setSelected(toolOfferSlot.equals(slot));
+ toolInspector.update(slot.getToolType(), slot.isLocked());
+ }
+
+ @Override
+ public void onBackKeyDown() {
+ gameActivity.getDataStorageHandler().writeUserData(gameActivity.getUser());
+ flipToCaller();
+ }
+
+ @Override
+ public void onClick(View v) {
+ ImageView toolSlotView = (ImageView) v;
+ int index = toolSlotViews.indexOf(toolSlotView);
+ if (canSelectedToolBePutInSlot(index)) {
+ slotSettings.changeToolSlotType(index, selectedToolOfferSlot.getToolType());
+ for (int i = 0; i < toolSlotViews.size(); i++) {
+ ToolSlot toolSlot = slotSettings.get(i);
+ ImageView view = toolSlotViews.get(i);
+ view.setImageResource(toolSlot.getDrawable());
+ }
+ }
+ }
+
+ private boolean canSelectedToolBePutInSlot(int slotIndex) {
+ return slotIndex != -1 && !slotSettings.get(slotIndex)
+ .isLocked() && selectedToolOfferSlot != null && selectedToolOfferSlot.getToolType()
+ .isBought();
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/screens/WorldsScreen.java b/app/src/main/java/de/frajul/endlessroll/main/screens/WorldsScreen.java
new file mode 100644
index 0000000..475ca36
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/screens/WorldsScreen.java
@@ -0,0 +1,61 @@
+package de.frajul.endlessroll.main.screens;
+
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.levels.LevelPack;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.views.TopBar;
+import de.frajul.endlessroll.views.WorldButton;
+import de.frajul.endlessroll.views.WorldButtonOnClickListener;
+
+/**
+ * Created by Julian on 07.07.2016.
+ */
+public class WorldsScreen extends Screen implements WorldButtonOnClickListener {
+
+ private List worldButtons = new ArrayList<>();
+ private TopBar topBar;
+ private LinearLayout buttonLayout;
+
+ public WorldsScreen(GameActivity gameActivity) {
+ super(ScreenType.WORLDS, gameActivity, R.layout.worlds);
+ topBar = super.createTopBar(R.id.worlds_topbar);
+ buttonLayout = (LinearLayout) layout.findViewById(R.id.worlds_layout);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ params.setMargins(25, 0, 25, 0);
+
+ for (LevelPack levelPack : gameActivity.getLevelManager()) {
+ WorldButton button = new WorldButton(gameActivity, levelPack, this);
+ buttonLayout.addView(button.getView(), params);
+ worldButtons.add(button);
+ }
+ }
+
+ @Override
+ public void prepareToBeShown() {
+ topBar.update();
+ for (WorldButton button : worldButtons)
+ button.update();
+ }
+
+ @Override
+ public void onBackKeyDown() {
+ flipTo(ScreenType.START);
+ }
+
+ @Override
+ public void onClick(WorldButton worldButton) {
+ LevelPack levelPack = worldButton.getLevelPack();
+ if (!levelPack.isLocked()) {
+ gameActivity.onWorldSelected(levelPack);
+ gameActivity.flipToScreen(ScreenType.LEVELS);
+ }
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/tutorial/BreakPoint.java b/app/src/main/java/de/frajul/endlessroll/main/tutorial/BreakPoint.java
new file mode 100644
index 0000000..a4eeee6
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/tutorial/BreakPoint.java
@@ -0,0 +1,47 @@
+package de.frajul.endlessroll.main.tutorial;
+
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+
+/**
+ * Created by Julian on 14.03.2017.
+ */
+
+public class BreakPoint {
+
+ public final static float LEVEL_FINISHED_X = -1;
+
+ private float x;
+ private boolean alreadyShown = false;
+ @StringRes
+ private int textId;
+ @DrawableRes
+ private int imageId;
+
+ public BreakPoint(float x, @StringRes int textId, @DrawableRes int imageId) {
+ this.x = x;
+ this.textId = textId;
+ this.imageId = imageId;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public int getTextId() {
+ return textId;
+ }
+
+ public int getImageId() {
+ return imageId;
+ }
+
+ public void setAlreadyShown(boolean alreadyShown) {
+ this.alreadyShown = alreadyShown;
+ }
+
+ public boolean isAlreadyShown() {
+ return alreadyShown;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/tutorial/Tutorial.java b/app/src/main/java/de/frajul/endlessroll/main/tutorial/Tutorial.java
new file mode 100644
index 0000000..6118407
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/tutorial/Tutorial.java
@@ -0,0 +1,72 @@
+package de.frajul.endlessroll.main.tutorial;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Created by Julian on 14.03.2017.
+ */
+
+public class Tutorial {
+
+ private int levelId, levelPackId;
+ private List breakPoints;
+ protected List currentBreakPoints = new ArrayList<>();
+
+ public Tutorial(int levelId, int levelPackId, BreakPoint... breakPoints) {
+ this.levelId = levelId;
+ this.levelPackId = levelPackId;
+ this.breakPoints = Arrays.asList(breakPoints);
+ }
+
+ public void reset() {
+ for (BreakPoint breakPoint : breakPoints)
+ breakPoint.setAlreadyShown(false);
+ currentBreakPoints.clear();
+ }
+
+ public void onLevelFinished() {
+ currentBreakPoints.clear();
+
+ for (BreakPoint breakPoint : breakPoints) {
+ if (breakPoint.getX() == BreakPoint.LEVEL_FINISHED_X) {
+ breakPoint.setAlreadyShown(true);
+ currentBreakPoints.add(breakPoint);
+ }
+ }
+ }
+
+ public void update(float playerProgress) {
+ playerProgress *= 2f;
+ currentBreakPoints.clear();
+
+ for (BreakPoint breakPoint : breakPoints) {
+ if (!breakPoint.isAlreadyShown() && playerProgress >= breakPoint.getX() && breakPoint
+ .getX() != BreakPoint.LEVEL_FINISHED_X) {
+ breakPoint.setAlreadyShown(true);
+ currentBreakPoints.add(breakPoint);
+ }
+ }
+ }
+
+ public boolean isOverNewBreakPoints() {
+ return !currentBreakPoints.isEmpty();
+ }
+
+ public List getCurrentBreakPoints() {
+ return currentBreakPoints;
+ }
+
+ public List getBreakPoints() {
+ return breakPoints;
+ }
+
+ public int getLevelId() {
+ return levelId;
+ }
+
+ public int getLevelPackId() {
+ return levelPackId;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/main/tutorial/TutorialManager.java b/app/src/main/java/de/frajul/endlessroll/main/tutorial/TutorialManager.java
new file mode 100644
index 0000000..e02913c
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/tutorial/TutorialManager.java
@@ -0,0 +1,56 @@
+package de.frajul.endlessroll.main.tutorial;
+
+import java.util.Arrays;
+import java.util.List;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.levels.Level;
+
+/**
+ * Created by Julian on 17.03.2017.
+ */
+
+public class TutorialManager {
+
+ private List gameTutorials;
+ private ToolShopTutorial toolShopTutorial;
+
+ public TutorialManager() {
+ Tutorial t11 = new Tutorial(1, 1, new BreakPoint(0, R.string.tutorial_welcome, -1),
+ new BreakPoint(0, R.string.tutorial_place_tools, R.drawable.tutorial_place_tools),
+ new BreakPoint(7, R.string.tutorial_place_ramp_gap,
+ R.drawable.tutorial_place_ramp_gap),
+ new BreakPoint(21, R.string.tutorial_place_ramp_obstacle,
+ R.drawable.tutorial_place_ramp_obstacle));
+ Tutorial t21 = new Tutorial(2, 1, new BreakPoint(11, R.string.tutorial_place_ramp_air,
+ R.drawable.tutorial_place_ramp_air_1),
+ new BreakPoint(33, R.string.tutorial_place_ramp_air_2,
+ R.drawable.tutorial_place_ramp_air_2));
+ Tutorial t51 = new Tutorial(5, 1,
+ new BreakPoint(BreakPoint.LEVEL_FINISHED_X, R.string.tutorial_leveled_up, -1),
+ new BreakPoint(BreakPoint.LEVEL_FINISHED_X, R.string.tutorial_to_toolshop,
+ R.drawable.tutorial_to_toolshop));
+
+ gameTutorials = Arrays.asList(t11, t21, t51);
+
+ toolShopTutorial = new ToolShopTutorial();
+ }
+
+ public void resetGameTutorials() {
+ for (Tutorial tutorial : gameTutorials)
+ tutorial.reset();
+ }
+
+ public Tutorial getGameTutorial(Level level) {
+ for (Tutorial tutorial : gameTutorials) {
+ if (tutorial.getLevelPackId() == level.getPackId() && tutorial.getLevelId() == level
+ .getId())
+ return tutorial;
+ }
+ return null;
+ }
+
+ public ToolShopTutorial getToolShopTutorial() {
+ return toolShopTutorial;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/main/tutorial/TutorialView.java b/app/src/main/java/de/frajul/endlessroll/main/tutorial/TutorialView.java
new file mode 100644
index 0000000..289c0dc
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/main/tutorial/TutorialView.java
@@ -0,0 +1,85 @@
+package de.frajul.endlessroll.main.tutorial;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.main.GameActivity;
+
+/**
+ * Created by Julian on 24.02.2017.
+ */
+
+public class TutorialView implements View.OnClickListener {
+
+ private View layout;
+ private TextView textView;
+ private ImageView imageView;
+ private GameActivity activity;
+
+ private List breakPoints;
+
+ public TutorialView(GameActivity activity) {
+ this.activity = activity;
+ LayoutInflater inflater = LayoutInflater.from(activity);
+ layout = inflater.inflate(R.layout.tutorial, null);
+ layout.setVisibility(View.GONE);
+ layout.setOnClickListener(this);
+ layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ textView = (TextView) layout.findViewById(R.id.tutorial_text_view);
+ textView.setTypeface(activity.getTypeface());
+ imageView = (ImageView) layout.findViewById(R.id.tutorial_image_view);
+ }
+
+ public View getLayout() {
+ return layout;
+ }
+
+ public void show(List breakPoints) {
+ this.breakPoints = breakPoints;
+ if(!breakPoints.isEmpty())
+ showFirstBreakPoint();
+ }
+
+ public boolean isShowingTutorial() {
+ return layout.getVisibility() == View.VISIBLE;
+ }
+
+ private void showFirstBreakPoint() {
+ int textId = breakPoints.get(0).getTextId();
+ int imageId = breakPoints.get(0).getImageId();
+
+ if (textId == -1)
+ textView.setVisibility(View.INVISIBLE);
+ else {
+ textView.setVisibility(View.VISIBLE);
+ textView.setText(textId);
+ }
+ if (imageId == -1)
+ imageView.setVisibility(View.INVISIBLE);
+ else {
+ imageView.setVisibility(View.VISIBLE);
+ imageView.setImageResource(imageId);
+ }
+ layout.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if(breakPoints.size() == 0)
+ return;
+ breakPoints.remove(0);
+ if (!breakPoints.isEmpty()) {
+ showFirstBreakPoint();
+ } else {
+ layout.setVisibility(View.GONE);
+ activity.onTutorialViewHidden();
+ }
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/Fbo.java b/app/src/main/java/de/frajul/endlessroll/rendering/Fbo.java
new file mode 100644
index 0000000..1f9a860
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/Fbo.java
@@ -0,0 +1,47 @@
+package de.frajul.endlessroll.rendering;
+
+import android.opengl.GLES20;
+
+import javax.microedition.khronos.opengles.GL11;
+
+/**
+ * Created by Julian on 06.08.2016.
+ */
+public class Fbo {
+
+ private int frameBuffer;
+ private int texture;
+
+ public Fbo(int width, int height) {
+ int[] frameBuffers = new int[1];
+ GLES20.glGenFramebuffers(1, frameBuffers, 0);
+ this.frameBuffer = frameBuffers[0];
+ bind();
+
+ int[] textures = new int[1];
+ GLES20.glGenTextures(1, textures, 0);
+ this.texture = textures[0];
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
+ GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GL11.GL_UNSIGNED_BYTE, null);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_NONE);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_NONE);
+ GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texture, 0);
+
+ unbind();
+ }
+
+ public void bind() {
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer);
+ }
+
+ public void unbind() {
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
+ }
+
+ public int getTexture() {
+ return texture;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/Lock.java b/app/src/main/java/de/frajul/endlessroll/rendering/Lock.java
new file mode 100644
index 0000000..c09a2c3
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/Lock.java
@@ -0,0 +1,22 @@
+package de.frajul.endlessroll.rendering;
+
+public class Lock {
+
+ private boolean isLocked = false;
+
+ public synchronized void lock() {
+ while (isLocked) {
+ try {
+ wait();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ isLocked = true;
+ }
+
+ public synchronized void unlock() {
+ isLocked = false;
+ notify();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/MatrixCreator.java b/app/src/main/java/de/frajul/endlessroll/rendering/MatrixCreator.java
new file mode 100644
index 0000000..9b21be8
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/MatrixCreator.java
@@ -0,0 +1,50 @@
+package de.frajul.endlessroll.rendering;
+
+import android.opengl.Matrix;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.main.game.Camera;
+
+/**
+ * Created by Julian on 23.11.2015.
+ */
+public class MatrixCreator {
+
+ private float width, height;
+
+ public void setMVPMSize(float width, float height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ public float[] createModelViewProjectionMatrix(Camera camera) {
+ float[] mvpMatrix = new float[16];
+ float[] projectionMatrix = new float[16];
+ float[] viewMatrix = new float[16];
+
+ float ratio = width / height;
+ Matrix.frustumM(projectionMatrix, 0, -ratio + camera.getX(), ratio + camera.getX(), -1 + camera.getY(), 1 + camera.getY(), 1, 2);
+ Matrix.setLookAtM(viewMatrix, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0);
+ Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
+ return mvpMatrix;
+ }
+
+ public float[] createTransformationMatrix(Entity entity) {
+ float width = entity.getWidth();
+ float height = entity.getHeight();
+ float rotation = entity.getRotation();
+ Vector position = entity.getPosition();
+ return createTransformationMatrix(width, height, rotation, position);
+ }
+
+ public float[] createTransformationMatrix(float width, float height, float rotation, Vector position) {
+ float[] transformationMatrix = new float[16];
+ Matrix.setIdentityM(transformationMatrix, 0);
+ Matrix.translateM(transformationMatrix, 0, position.x, position.y, 0);
+ Matrix.rotateM(transformationMatrix, 0, rotation, 0, 0, 1);
+ Matrix.scaleM(transformationMatrix, 0, width * .5f, height * .5f, 0);
+ return transformationMatrix;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/Quad.java b/app/src/main/java/de/frajul/endlessroll/rendering/Quad.java
new file mode 100644
index 0000000..8daa0b2
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/Quad.java
@@ -0,0 +1,49 @@
+package de.frajul.endlessroll.rendering;
+
+import android.opengl.GLES20;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * Created by Julian on 26.11.2015.
+ */
+public class Quad {
+
+ private FloatBuffer vertexBuffer;
+ private FloatBuffer textureBuffer;
+
+ private float vertices[] = {1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
+ 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f};
+
+ private float textures[] = {1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f};
+
+ public Quad() {
+ vertexBuffer = createEmptyFloatBuffer();
+ vertexBuffer.put(vertices);
+ vertexBuffer.position(0);
+
+ textureBuffer = createEmptyFloatBuffer();
+ textureBuffer.put(textures);
+ textureBuffer.position(0);
+ }
+
+ private FloatBuffer createEmptyFloatBuffer() {
+ ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
+ bb.order(ByteOrder.nativeOrder());
+ return bb.asFloatBuffer();
+ }
+
+ public void draw() {
+ GLES20.glEnableVertexAttribArray(0);
+ GLES20.glEnableVertexAttribArray(1);
+ GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer);
+ GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertices.length / 2);
+ GLES20.glDisableVertexAttribArray(1);
+ GLES20.glDisableVertexAttribArray(0);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/Rendering.java b/app/src/main/java/de/frajul/endlessroll/rendering/Rendering.java
new file mode 100644
index 0000000..eaefeac
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/Rendering.java
@@ -0,0 +1,51 @@
+package de.frajul.endlessroll.rendering;
+
+import android.content.Context;
+import android.view.View;
+
+import de.frajul.endlessroll.data.Vector;
+import de.frajul.endlessroll.entities.textures.TexturePack;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.game.Scene;
+import de.frajul.endlessroll.main.game.Timer;
+
+/**
+ * Created by Julian on 26.11.2015.
+ */
+public abstract class Rendering implements View.OnTouchListener {
+
+ protected S scene;
+ private GameActivity gameActivity;
+ private boolean alreadyInitiated = false;
+
+ public Rendering(GameActivity gameActivity) {
+ this.gameActivity = gameActivity;
+ }
+
+ public void initiate(TexturePack texturePack, Timer timer) {
+ this.scene = init(texturePack, timer, !alreadyInitiated);
+ alreadyInitiated = true;
+ }
+
+ protected abstract S init(TexturePack texturePack, Timer timer, boolean isFirstTime);
+
+ public abstract void update();
+
+ public void onException(Exception e) {
+ gameActivity.onException(e);
+ }
+
+ public void setScreenSize(int width, int height) {
+ if (scene != null)
+ scene.setScreenSize(new Vector(width, height));
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+
+ public Context getContext() {
+ return gameActivity;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/renderer/GameRenderer.java b/app/src/main/java/de/frajul/endlessroll/rendering/renderer/GameRenderer.java
new file mode 100644
index 0000000..8c1555e
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/renderer/GameRenderer.java
@@ -0,0 +1,212 @@
+package de.frajul.endlessroll.rendering.renderer;
+
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.entities.Obstacle;
+import de.frajul.endlessroll.entities.textures.TexturePack;
+import de.frajul.endlessroll.entities.tileLists.Tile;
+import de.frajul.endlessroll.entities.tileLists.TileList;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.GameLog;
+import de.frajul.endlessroll.main.game.Camera;
+import de.frajul.endlessroll.main.game.Scene;
+import de.frajul.endlessroll.main.game.Timer;
+import de.frajul.endlessroll.rendering.MatrixCreator;
+import de.frajul.endlessroll.rendering.Quad;
+import de.frajul.endlessroll.rendering.Rendering;
+import de.frajul.endlessroll.rendering.shader.EntityShader;
+import de.frajul.endlessroll.rendering.shader.ObstacleShader;
+import de.frajul.endlessroll.rendering.shader.SimpleShader;
+import de.frajul.endlessroll.rendering.shader.TerrainShader;
+
+/**
+ * Created by Julian on 22.11.2015.
+ */
+public class GameRenderer implements GLSurfaceView.Renderer {
+
+ private List renderTargets = new ArrayList<>();
+ private Rendering currentRendering;
+ private MatrixCreator matrixCreator;
+
+ private GameActivity activity;
+ private Quad quad;
+
+ private ParticleRenderer particleRenderer;
+
+ private EntityShader entityShader;
+ private ObstacleShader obstacleShader;
+ private TerrainShader terrainShader;
+ private SimpleShader simpleShader;
+
+ private TexturePack texturePack;
+ private Timer timer;
+
+ public GameRenderer(GameActivity activity) {
+ this.activity = activity;
+ matrixCreator = new MatrixCreator();
+ quad = new Quad();
+ particleRenderer = new ParticleRenderer(activity, quad, matrixCreator);
+ }
+
+ public void addRendering(Rendering rendering) {
+ renderTargets.add(rendering);
+ }
+
+ public synchronized void setCurrentRendering(Rendering currentRendering) {
+ this.currentRendering = currentRendering;
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ GameLog.d("onSurfaceCreated");
+ GLES20.glClearColor(1, 1, 1, 1.0f);
+ gl.glEnable(GL10.GL_BLEND);
+ gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
+
+ try {
+ particleRenderer.createContext();
+ entityShader = new EntityShader(activity);
+ obstacleShader = new ObstacleShader(activity);
+ terrainShader = new TerrainShader(activity);
+ simpleShader = new SimpleShader(activity);
+ texturePack = new TexturePack(activity);
+ timer = new Timer();
+ } catch (Exception e) {
+ for (Rendering rendering : renderTargets)
+ rendering.onException(e);
+ }
+ for (Rendering rendering : renderTargets) {
+ rendering.initiate(texturePack, timer);
+ }
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ GameLog.d("onSurfaceChanged: width=" + width + ", height=" + height);
+ particleRenderer.createFbo(width, height);
+ GLES20.glViewport(0, 0, width, height);
+ matrixCreator.setMVPMSize(width, height);
+ for (Rendering rendering : renderTargets)
+ rendering.setScreenSize(width, height);
+ activity.onSurfaceChanged();
+ }
+
+ @Override
+ public synchronized void onDrawFrame(GL10 gl) {
+ timer.update();
+ if (currentRendering != null) {
+ currentRendering.update();
+ Scene scene = currentRendering.getScene();
+
+ particleRenderer.renderParticlesToFbo(gl, scene);
+
+ GLES20.glClearColor(1, 1, 1, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+
+ renderBackground(gl, scene);
+ renderTileList(gl, scene.getTerrain(), scene);
+ renderTileList(gl, scene.getCeiling(), scene);
+ renderEntityList(gl, scene.getCollectables(), scene.getCamera());
+ renderObstacles(gl, scene);
+ renderEntityList(gl, scene.getTools(), scene.getCamera());
+
+ renderEntityList(gl, scene.getUncategorizedEntities(), scene.getCamera());
+
+ renderFbo(gl);
+ }
+ }
+
+ private void renderBackground(GL10 gl, Scene scene) {
+ entityShader.start();
+ entityShader.loadMVPMatrix(matrixCreator, scene.getCamera());
+ synchronized (scene.getBackground()) {
+ for (Entity backgroundPart : scene.getBackground())
+ renderEntity(gl, backgroundPart);
+ }
+ entityShader.stop();
+ }
+
+ private void renderObstacles(GL10 gl, Scene scene) {
+ List obstacles = scene.getObstacles();
+ obstacleShader.start();
+ obstacleShader.loadMVPMatrix(matrixCreator, scene.getCamera());
+ synchronized (obstacles) {
+ for (Obstacle obstacle : obstacles)
+ renderObstacle(gl, obstacle);
+ }
+ obstacleShader.stop();
+ }
+
+
+ private void renderObstacle(GL10 gl, Obstacle obstacle) {
+ gl.glActiveTexture(GL10.GL_TEXTURE0);
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, obstacle.getTexture().getId());
+ obstacleShader.loadTransformationMatrix(matrixCreator, obstacle);
+ obstacleShader.loadAlpha(obstacle.getAlpha());
+ obstacleShader.loadTextureAtlasInfos(obstacle.getTexture());
+ obstacleShader.loadGridSize(obstacle.getGridSize());
+ obstacleShader.loadDeadly(obstacle.isDeadly());
+ obstacleShader.loadFloating(obstacle.isFloating());
+ quad.draw();
+ }
+
+ private void renderEntityList(GL10 gl, List extends Entity> list, Camera camera) {
+ entityShader.start();
+ entityShader.loadMVPMatrix(matrixCreator, camera);
+ synchronized (list) {
+ for (Entity entity : list) {
+ renderEntity(gl, entity);
+ }
+ }
+ entityShader.stop();
+ }
+
+ private void renderEntity(GL10 gl, Entity entity) {
+ if (!entity.isVisible())
+ return;
+
+ gl.glActiveTexture(GL10.GL_TEXTURE0);
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, entity.getTexture().getId());
+ entityShader.loadTransformationMatrix(matrixCreator, entity);
+ entityShader.loadAlpha(entity.getAlpha());
+ entityShader.loadTextureAtlasInfos(entity.getTexture(), entity.getTextureAtlasIndex());
+ float texScaleX = entity.getMaxTexSize().x == 0 ? 1 : entity.getWidth() / entity
+ .getMaxTexSize().x;
+ float texScaleY = entity.getMaxTexSize().y == 0 ? 1 : entity.getHeight() / entity
+ .getMaxTexSize().y;
+ entityShader.loadTexCoordScaling(texScaleX, texScaleY);
+ quad.draw();
+ }
+
+ private void renderTileList(GL10 gl, TileList tileList, Scene scene) {
+ terrainShader.start();
+ terrainShader.loadMVPMatrix(matrixCreator, scene.getCamera());
+ synchronized (tileList) {
+ for (Tile tile : tileList) {
+ gl.glActiveTexture(GL10.GL_TEXTURE0);
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, tile.getTexture().getId());
+ terrainShader.loadTransformationMatrix(matrixCreator, tile);
+ quad.draw();
+ }
+ }
+ terrainShader.stop();
+ }
+
+ private void renderFbo(GL10 gl) {
+ simpleShader.start();
+ gl.glActiveTexture(GL10.GL_TEXTURE0);
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, particleRenderer.getFboTexture());
+ quad.draw();
+ simpleShader.stop();
+ }
+
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/renderer/ParticleRenderer.java b/app/src/main/java/de/frajul/endlessroll/rendering/renderer/ParticleRenderer.java
new file mode 100644
index 0000000..368a384
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/renderer/ParticleRenderer.java
@@ -0,0 +1,106 @@
+package de.frajul.endlessroll.rendering.renderer;
+
+import android.content.Context;
+import android.opengl.GLES20;
+
+import de.frajul.endlessroll.entities.particles.Particle;
+import de.frajul.endlessroll.entities.particles.ParticleEffect;
+import de.frajul.endlessroll.entities.particles.ParticleSource;
+import de.frajul.endlessroll.main.GameLog;
+import de.frajul.endlessroll.main.game.Scene;
+import de.frajul.endlessroll.rendering.Fbo;
+import de.frajul.endlessroll.rendering.MatrixCreator;
+import de.frajul.endlessroll.rendering.Quad;
+import de.frajul.endlessroll.rendering.shader.ParticleShader;
+
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Created by Julian on 10.08.2016.
+ */
+public class ParticleRenderer {
+
+ private Context context;
+ private Quad quad;
+ private MatrixCreator matrixCreator;
+ private boolean additiveBlending = false;
+
+ private ParticleShader particleShader;
+ private Fbo fbo;
+
+ public ParticleRenderer(Context context, Quad quad, MatrixCreator matrixCreator) {
+ this.context = context;
+ this.quad = quad;
+ this.matrixCreator = matrixCreator;
+ }
+
+ public void createContext() throws Exception {
+ particleShader = new ParticleShader(context);
+ }
+
+ public void createFbo(int width, int height) {
+ fbo = new Fbo(width, height);
+ }
+
+ public void renderParticlesToFbo(GL10 gl, Scene scene) {
+ fbo.bind();
+ GLES20.glClearColor(0, 0, 0, 0.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+
+ particleShader.start();
+ particleShader.loadMVPMatrix(matrixCreator, scene.getCamera());
+ synchronized (scene.getParticleSystem().getEffects()) {
+ for (ParticleEffect effect : scene.getParticleSystem().getEffects()) {
+ gl.glActiveTexture(GL10.GL_TEXTURE0);
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, effect.getTexture().getId());
+
+ switchAdditiveBlending(gl, effect.getOptions().isAdditive());
+ synchronized (effect.getSources()) {
+ for (ParticleSource source : effect.getSources()) {
+ source.getActiveParticleLock().lock();
+ for (Particle particle : source.getActiveParticles())
+ renderParticle(particle);
+ source.getActiveParticleLock().unlock();
+ }
+ }
+ }
+ }
+ disableAdditiveBlending(gl);
+
+ particleShader.stop();
+ fbo.unbind();
+ }
+
+
+ private void renderParticle(Particle particle) {
+ particleShader.loadTransformationMatrix(matrixCreator, particle);
+ particleShader.loadColor(particle.getColor());
+ particleShader.loadAlpha(particle.getAlpha());
+ quad.draw();
+ }
+
+ private void switchAdditiveBlending(GL10 gl, boolean additive) {
+ if (additive && !additiveBlending)
+ enableAdditiveBlending(gl);
+ if (!additive && additiveBlending)
+ disableAdditiveBlending(gl);
+ }
+
+ private void enableAdditiveBlending(GL10 gl) {
+ if (!additiveBlending) {
+ additiveBlending = true;
+ gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
+ }
+ }
+
+ private void disableAdditiveBlending(GL10 gl) {
+ if (additiveBlending) {
+ additiveBlending = false;
+ gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+
+ public int getFboTexture() {
+ return fbo.getTexture();
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/shader/EntityShader.java b/app/src/main/java/de/frajul/endlessroll/rendering/shader/EntityShader.java
new file mode 100644
index 0000000..0c16c71
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/shader/EntityShader.java
@@ -0,0 +1,60 @@
+package de.frajul.endlessroll.rendering.shader;
+
+import android.content.Context;
+import android.opengl.GLES20;
+
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.entities.textures.Texture;
+import de.frajul.endlessroll.main.game.Camera;
+import de.frajul.endlessroll.rendering.MatrixCreator;
+
+/**
+ * Created by Julian on 10.08.2016.
+ */
+public class EntityShader extends ShaderProgram {
+
+ private int location_mvpMatrix;
+ private int location_transformationMatrix;
+ private int location_alpha;
+ private int location_texAtlasSize;
+ private int location_texAtlasIndex;
+ private int location_texCoordScaling;
+
+ public EntityShader(Context context) throws Exception {
+ super(context, "shader/entityVertexShader.glsl", "shader/entityFragmentShader.glsl");
+ }
+
+ @Override
+ protected void loadUniformLocations() {
+ location_mvpMatrix = super.getUniformLocation("mvpMatrix");
+ location_transformationMatrix = super.getUniformLocation("transformationMatrix");
+ location_alpha = super.getUniformLocation("alpha");
+ location_texAtlasSize = super.getUniformLocation("texAtlasSize");
+ location_texAtlasIndex = super.getUniformLocation("texAtlasIndex");
+ location_texCoordScaling = super.getUniformLocation("texCoordScaling");
+ }
+
+ public void loadMVPMatrix(MatrixCreator matrixCreator, Camera camera) {
+ float[] mvpMatrix = matrixCreator.createModelViewProjectionMatrix(camera);
+ GLES20.glUniformMatrix4fv(location_mvpMatrix, 1, false, mvpMatrix, 0);
+ }
+
+ public void loadTransformationMatrix(MatrixCreator matrixCreator, Entity entity) {
+ float[] transformationMatrix = matrixCreator.createTransformationMatrix(entity);
+ GLES20.glUniformMatrix4fv(location_transformationMatrix, 1, false, transformationMatrix, 0);
+ }
+
+ public void loadAlpha(float alpha) {
+ GLES20.glUniform1f(location_alpha, alpha);
+ }
+
+ public void loadTextureAtlasInfos(Texture texture, int atlasIndex) {
+ GLES20.glUniform2f(location_texAtlasSize, texture.getAtlasWidth(), texture.getAtlasHeight());
+ GLES20.glUniform1f(location_texAtlasIndex, atlasIndex);
+ }
+
+ public void loadTexCoordScaling(float scalingX, float scalingY){
+ GLES20.glUniform2f(location_texCoordScaling, scalingX, scalingY);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/shader/ParticleShader.java b/app/src/main/java/de/frajul/endlessroll/rendering/shader/ParticleShader.java
new file mode 100644
index 0000000..fab4e05
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/shader/ParticleShader.java
@@ -0,0 +1,51 @@
+package de.frajul.endlessroll.rendering.shader;
+
+import android.content.Context;
+import android.opengl.GLES20;
+
+import de.frajul.endlessroll.data.Color;
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.main.game.Camera;
+import de.frajul.endlessroll.rendering.MatrixCreator;
+
+/**
+ * Created by Julian on 10.08.2016.
+ */
+public class ParticleShader extends ShaderProgram {
+
+ private int location_mvpMatrix;
+ private int location_transformationMatrix;
+ private int location_color;
+ private int location_alpha;
+
+ public ParticleShader(Context context) throws Exception {
+ super(context, "shader/entityVertexShader.glsl", "shader/particleFragmentShader.glsl");
+ }
+
+ @Override
+ protected void loadUniformLocations() {
+ location_mvpMatrix = super.getUniformLocation("mvpMatrix");
+ location_transformationMatrix = super.getUniformLocation("transformationMatrix");
+ location_color = super.getUniformLocation("color");
+ location_alpha = super.getUniformLocation("alpha");
+ }
+
+ public void loadMVPMatrix(MatrixCreator matrixCreator, Camera camera) {
+ float[] mvpMatrix = matrixCreator.createModelViewProjectionMatrix(camera);
+ GLES20.glUniformMatrix4fv(location_mvpMatrix, 1, false, mvpMatrix, 0);
+ }
+
+ public void loadTransformationMatrix(MatrixCreator matrixCreator, Entity entity) {
+ float[] transformationMatrix = matrixCreator.createTransformationMatrix(entity);
+ GLES20.glUniformMatrix4fv(location_transformationMatrix, 1, false, transformationMatrix, 0);
+ }
+
+ public void loadAlpha(float alpha){
+ GLES20.glUniform1f(location_alpha, alpha);
+ }
+
+ public void loadColor(Color color) {
+ GLES20.glUniform3f(location_color, color.getR(), color.getG(), color.getB());
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/shader/ShaderProgram.java b/app/src/main/java/de/frajul/endlessroll/rendering/shader/ShaderProgram.java
new file mode 100644
index 0000000..dd85816
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/shader/ShaderProgram.java
@@ -0,0 +1,89 @@
+package de.frajul.endlessroll.rendering.shader;
+
+import android.content.Context;
+import android.opengl.GLES20;
+
+import de.frajul.endlessroll.main.GameLog;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * Created by Julian on 23.11.2015.
+ */
+public abstract class ShaderProgram {
+
+ private Context context;
+ private int vertexShader, fragmentShader, program;
+
+ public ShaderProgram(Context context, String vertexShaderPath, String fragmentShaderPath) throws Exception {
+ this.context = context;
+ vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderPath);
+ fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderPath);
+ program = GLES20.glCreateProgram();
+ GLES20.glAttachShader(program, vertexShader);
+ GLES20.glAttachShader(program, fragmentShader);
+ GLES20.glLinkProgram(program);
+ int[] linkStatus = new int[1];
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+ if (linkStatus[0] == GLES20.GL_FALSE) {
+ GLES20.glDeleteProgram(program);
+ throw new Exception("Could not link program: "
+ + GLES20.glGetProgramInfoLog(program));
+ }
+
+ bindAttribLocations();
+ loadUniformLocations();
+ GameLog.d("ShaderProgram successfully loaded");
+ }
+
+ private void bindAttribLocations() {
+ GLES20.glBindAttribLocation(program, 0, "position");
+ GLES20.glBindAttribLocation(program, 1, "texCoords");
+ }
+
+ protected abstract void loadUniformLocations();
+
+ protected int getUniformLocation(String name) {
+ return GLES20.glGetUniformLocation(program, name);
+ }
+
+ public void start() {
+ GLES20.glUseProgram(program);
+ }
+
+ public void stop() {
+ GLES20.glUseProgram(0);
+ }
+
+
+ private int loadShader(int type, String shaderName) throws Exception {
+ try {
+ InputStream is = context.getAssets().open(shaderName);
+ InputStreamReader isReader = new InputStreamReader(is);
+ BufferedReader reader = new BufferedReader(isReader);
+ StringBuilder source = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null)
+ source.append(line);
+
+ int shader = GLES20.glCreateShader(type);
+ GLES20.glShaderSource(shader, source.toString());
+ GLES20.glCompileShader(shader);
+
+ int[] compiled = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+ if (compiled[0] == GLES20.GL_FALSE) {
+ GLES20.glDeleteShader(shader);
+ throw new Exception("Could not compile shader \"" + shaderName + "\": "
+ + GLES20.glGetShaderInfoLog(shader));
+ }
+ GameLog.d("Shader \"" + shaderName + "\" successfully loaded");
+ return shader;
+ } catch (Exception e) {
+ throw new Exception("Could not load Shader \"" + shaderName + "\"", e);
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/shader/SimpleShader.java b/app/src/main/java/de/frajul/endlessroll/rendering/shader/SimpleShader.java
new file mode 100644
index 0000000..a30e545
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/shader/SimpleShader.java
@@ -0,0 +1,17 @@
+package de.frajul.endlessroll.rendering.shader;
+
+import android.content.Context;
+
+/**
+ * Created by Julian on 10.08.2016.
+ */
+public class SimpleShader extends ShaderProgram {
+
+ public SimpleShader(Context context) throws Exception {
+ super(context, "shader/simpleVertexShader.glsl", "shader/simpleFragmentShader.glsl");
+ }
+
+ @Override
+ protected void loadUniformLocations() {
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/rendering/shader/TerrainShader.java b/app/src/main/java/de/frajul/endlessroll/rendering/shader/TerrainShader.java
new file mode 100644
index 0000000..07c527e
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/rendering/shader/TerrainShader.java
@@ -0,0 +1,38 @@
+package de.frajul.endlessroll.rendering.shader;
+
+import android.content.Context;
+import android.opengl.GLES20;
+
+import de.frajul.endlessroll.entities.Entity;
+import de.frajul.endlessroll.main.game.Camera;
+import de.frajul.endlessroll.rendering.MatrixCreator;
+
+/**
+ * Created by Julian on 10.08.2016.
+ */
+public class TerrainShader extends ShaderProgram {
+
+ private int location_mvpMatrix;
+ private int location_transformationMatrix;
+
+ public TerrainShader(Context context) throws Exception {
+ super(context, "shader/terrainVertexShader.glsl", "shader/simpleFragmentShader.glsl");
+ }
+
+ @Override
+ protected void loadUniformLocations() {
+ location_mvpMatrix = super.getUniformLocation("mvpMatrix");
+ location_transformationMatrix = super.getUniformLocation("transformationMatrix");
+ }
+
+ public void loadMVPMatrix(MatrixCreator matrixCreator, Camera camera) {
+ float[] mvpMatrix = matrixCreator.createModelViewProjectionMatrix(camera);
+ GLES20.glUniformMatrix4fv(location_mvpMatrix, 1, false, mvpMatrix, 0);
+ }
+
+ public void loadTransformationMatrix(MatrixCreator matrixCreator, Entity entity) {
+ float[] transformationMatrix = matrixCreator.createTransformationMatrix(entity);
+ GLES20.glUniformMatrix4fv(location_transformationMatrix, 1, false, transformationMatrix, 0);
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/sounds/Music.java b/app/src/main/java/de/frajul/endlessroll/sounds/Music.java
new file mode 100644
index 0000000..810105f
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/sounds/Music.java
@@ -0,0 +1,79 @@
+package de.frajul.endlessroll.sounds;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+
+import de.frajul.endlessroll.main.ExceptionHandler;
+
+/**
+ * Created by Julian on 18.12.2015.
+ */
+public class Music {
+
+ private ExceptionHandler handler;
+ private MediaPlayer player;
+ private boolean mute;
+ private boolean paused = true;
+ private int pausedPosition;
+
+ public Music(ExceptionHandler handler, Context context, int id) {
+ this.handler = handler;
+ player = MediaPlayer.create(context, id);
+ }
+
+ public void setMute(boolean mute) {
+ this.mute = mute;
+ if (mute && !paused) {
+ stop();
+ paused = false;
+ }
+ if (!mute && !paused)
+ start();
+ }
+
+ public void pause() {
+ try {
+ player.pause();
+ paused = true;
+ pausedPosition = player.getCurrentPosition();
+ } catch (Exception e) {
+ handler.onException(e);
+ }
+ }
+
+ public void start() {
+ try {
+ if (!mute)
+ player.start();
+ paused = false;
+ } catch (Exception e) {
+ handler.onException(e);
+ }
+ }
+
+ public void stop() {
+ try {
+ player.pause();
+ player.seekTo(0);
+ paused = true;
+ } catch (Exception e) {
+ handler.onException(e);
+ }
+ }
+
+
+ public void resume() {
+ try {
+ if (paused) {
+ player.seekTo(pausedPosition);
+ start();
+ }
+ } catch (Exception e) {
+ handler.onException(e);
+ }
+ }
+
+ public MediaPlayer getPlayer() {
+ return player;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/sounds/SoundManager.java b/app/src/main/java/de/frajul/endlessroll/sounds/SoundManager.java
new file mode 100644
index 0000000..6af479f
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/sounds/SoundManager.java
@@ -0,0 +1,78 @@
+package de.frajul.endlessroll.sounds;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.SoundPool;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.GameLog;
+
+/**
+ * Created by Julian on 18.12.2015.
+ */
+public class SoundManager {
+
+ private Context context;
+ private SoundPool soundPool;
+ private boolean soundOn;
+
+ public final Music backgroundMusic;
+
+ public SoundManager(GameActivity activity) {
+ this.context = activity;
+ soundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 0);
+
+ backgroundMusic = new Music(activity, activity, R.raw.background);
+ ToolType.loadAllPlacingSounds(this);
+ }
+
+ public void setSoundOn(boolean on) {
+ this.soundOn = on;
+ backgroundMusic.setMute(!on);
+ }
+
+ public boolean isSoundOn() {
+ return soundOn;
+ }
+
+ public void pause() {
+ soundPool.autoPause();
+ backgroundMusic.pause();
+ }
+
+ public void resume() {
+ soundPool.autoResume();
+ backgroundMusic.resume();
+ }
+
+ public void destroy() {
+ backgroundMusic.getPlayer().release();
+ soundPool.release();
+ }
+
+ public int loadSound(int id) {
+ int sound = soundPool.load(context, id, 1);
+ return sound;
+ }
+
+ public void playSound(int id) {
+ float volume = getVolume();
+ int feedback = soundPool.play(id, volume, volume, 1, 0, 1);
+ if (feedback == 0)
+ GameLog.e("SoundId: " + id + " cannot be played");
+ }
+
+ private float getVolume() {
+ if (soundOn) {
+ AudioManager manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ float volume =
+ manager.getStreamVolume(AudioManager.STREAM_MUSIC);
+ float maxVolume = manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ return volume / maxVolume;
+ }
+ return 0;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/sqlDatabase/DatabaseAdapter.java b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/DatabaseAdapter.java
new file mode 100644
index 0000000..91b532c
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/DatabaseAdapter.java
@@ -0,0 +1,88 @@
+package de.frajul.endlessroll.sqlDatabase;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import de.frajul.endlessroll.main.GameLog;
+
+/**
+ * Created by Julian on 07.06.2016.
+ */
+public abstract class DatabaseAdapter extends SQLiteOpenHelper {
+
+ private SQLTable[] tables;
+ private SQLiteDatabase database;
+
+ public DatabaseAdapter(Context context, String databaseName, int version) {
+ super(context, databaseName, null, version);
+ createColumns();
+ this.tables = createTables();
+ }
+
+ protected abstract void createColumns();
+
+ protected abstract SQLTable[] createTables();
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ GameLog.i("DATABASE CREATE");
+ for (SQLTable table : tables)
+ db.execSQL(table.getSqlCreateTable());
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ for (SQLTable table : tables)
+ db.execSQL(table.getSqlDeleteTable());
+ onCreate(db);
+ }
+
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ onUpgrade(db, oldVersion, newVersion);
+ }
+
+ public void open() throws SQLException {
+ database = getWritableDatabase();
+ }
+
+ protected void deleteTableEntry(SQLTable table, SQLTableColumn c0, int c0Condition, SQLTableColumn c1, int c1Condition) {
+ database.execSQL("DELETE FROM " + table.getName() + " WHERE " + c0.getKey() + " = " + c0Condition + " AND " + c1.getKey() + " = " + c1Condition);
+ }
+
+ protected void clearTable(SQLTable table) {
+ database.execSQL(table.getSqlClearTable());
+ }
+
+ protected long insertData(SQLTable table, ContentValues data) {
+ return database.insert(table.getName(), null, data);
+ }
+
+ protected long update(SQLTable table, ContentValues data, SQLTableColumn c0, int c0Condition, SQLTableColumn c1, int c1Condition) {
+ int reached = database.delete(table.getName(), c0.getKey() + "=" + c0Condition + " and " + c1.getKey() + "=" + c1Condition, null);
+ return insertData(table, data);
+ }
+
+ protected long update(SQLTable table, ContentValues data, SQLTableColumn c0, int c0Condition) {
+ int reached = database.delete(table.getName(), c0.getKey() + "=" + c0Condition, null);
+ return insertData(table, data);
+ }
+
+ protected Cursor getCursor(SQLTable table) {
+ return database.rawQuery(table.getSqlGetCursor(), null);
+ }
+
+ protected Cursor getCursor(SQLTable table, SQLTableColumn c0, int c0Condition, SQLTableColumn c1, int c1Condition) {
+ return database.rawQuery(table.getSqlGetCursor() + " where " + c0.getKey() + "=" + c0Condition + " and " + c1.getKey() + "=" + c1Condition, null);
+ }
+
+ protected Cursor getCursor(SQLTable table, SQLTableColumn c, int cCondition) {
+ return database.rawQuery(table.getSqlGetCursor() + " where " + c.getKey() + "=" + cCondition, null);
+ }
+
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/sqlDatabase/MyDatabase.java b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/MyDatabase.java
new file mode 100644
index 0000000..c5774ba
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/MyDatabase.java
@@ -0,0 +1,131 @@
+package de.frajul.endlessroll.sqlDatabase;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.levels.Level;
+import de.frajul.endlessroll.levels.LevelPack;
+
+/**
+ * Created by Julian on 10.05.2016.
+ */
+public class MyDatabase extends DatabaseAdapter {
+
+ private final static int VERSION = 9;
+ private final static String DATABASE_NAME = "DATABASE";
+ private SQLTableColumn levelPackIdColumn, lockedColumn;
+ private SQLTableColumn levelIdColumn, levelFinishedColumn, collectedStarsColumn, collectedEnergyColumn;
+ private SQLTableColumn toolIdColumn, boughtColumn, toolLevelColumn;
+ private SQLTable levelPackTable, levelTable, toolTable;
+
+ public MyDatabase(Context context) {
+ super(context, DATABASE_NAME, VERSION);
+ }
+
+ @Override
+ protected void createColumns() {
+ levelPackIdColumn = new SQLTableColumn("LEVELPACK_ID", SQLTableColumn.ColumnType.INTEGER);
+ lockedColumn = new SQLTableColumn("LOCKED", SQLTableColumn.ColumnType.INTEGER);
+
+ levelIdColumn = new SQLTableColumn("LEVEL_ID", SQLTableColumn.ColumnType.INTEGER);
+ levelFinishedColumn = new SQLTableColumn("FINISHED", SQLTableColumn.ColumnType.INTEGER);
+ collectedStarsColumn = new SQLTableColumn("STARS", SQLTableColumn.ColumnType.TEXT);
+ collectedEnergyColumn = new SQLTableColumn("ENERGY", SQLTableColumn.ColumnType.INTEGER);
+
+ toolIdColumn = new SQLTableColumn("TOOL_ID", SQLTableColumn.ColumnType.INTEGER);
+ boughtColumn = new SQLTableColumn("BOUGHT", SQLTableColumn.ColumnType.INTEGER);
+ toolLevelColumn = new SQLTableColumn("TOOL_LEVEL", SQLTableColumn.ColumnType.INTEGER);
+ }
+
+ @Override
+ protected SQLTable[] createTables() {
+ levelPackTable = new SQLTable("LEVELPACK_TABLE", levelPackIdColumn, lockedColumn);
+ levelTable = new SQLTable("LEVEL_TABLE", levelPackIdColumn, levelIdColumn, lockedColumn,
+ levelFinishedColumn, collectedStarsColumn, collectedEnergyColumn);
+ toolTable = new SQLTable("TOOL_TABLE", toolIdColumn, boughtColumn, toolLevelColumn);
+ return new SQLTable[]{levelPackTable, levelTable, toolTable};
+ }
+
+ public void writeLevelPackLocked(LevelPack levelPack) {
+ ContentValues values = new ContentValues();
+ values.put(levelPackIdColumn.getKey(), levelPack.getId());
+ values.put(lockedColumn.getKey(), levelPack.isLocked() ? 1 : 0);
+ super.update(levelPackTable, values, levelPackIdColumn, levelPack.getId());
+ }
+
+ public void readLevelPackLocked(LevelPack levelPack) {
+ Cursor cursor = super.getCursor(levelPackTable, levelPackIdColumn, levelPack.getId());
+ if (cursor.moveToFirst()) {
+ boolean locked = cursor.getInt(1) == 1;
+ levelPack.setLocked(locked);
+ }
+ cursor.close();
+ }
+
+ public void clearLevelPackLocked() {
+ super.clearTable(levelPackTable);
+ }
+
+ public void writeLevelProgress(Level level) {
+ ContentValues values = new ContentValues();
+ values.put(levelPackIdColumn.getKey(), level.getPackId());
+ values.put(levelIdColumn.getKey(), level.getId());
+ values.put(lockedColumn.getKey(), level.isLocked() ? 1 : 0);
+ values.put(levelFinishedColumn.getKey(), level.isFinished() ? 1 : 0);
+ values.put(collectedStarsColumn.getKey(), level.getCollectedStarCodeForSQL());
+ values.put(collectedEnergyColumn.getKey(), level.isEnergyCollected());
+ super.update(levelTable, values, levelPackIdColumn, level.getPackId(), levelIdColumn,
+ level.getId());
+ }
+
+ public void readLevelProgress(Level level) {
+ Cursor cursor = super
+ .getCursor(levelTable, levelPackIdColumn, level.getPackId(), levelIdColumn,
+ level.getId());
+ if (cursor.moveToFirst()) {
+ boolean locked = cursor.getInt(2) == 1;
+ boolean finished = cursor.getInt(3) == 1;
+ String stars = cursor.getString(4);
+ boolean energy = cursor.getInt(5) == 1;
+
+ level.setLocked(locked);
+ level.setFinished(finished);
+ level.setCollectedStarsFromSQL(stars);
+ level.setEnergyCollected(energy);
+ }
+ cursor.close();
+ }
+
+ public void clearLevelProgess() {
+ super.clearTable(levelTable);
+ }
+
+ public void writeToolData() {
+ for (ToolType tool : ToolType.values()) {
+ ContentValues values = new ContentValues();
+ values.put(toolIdColumn.getKey(), tool.ordinal());
+ values.put(boughtColumn.getKey(), tool.isBought() ? 1 : 0);
+ values.put(toolLevelColumn.getKey(), tool.getCurrentUpgradeLevel());
+ super.update(toolTable, values, toolIdColumn, tool.ordinal());
+ }
+ }
+
+ public void readToolData() {
+ Cursor cursor = super.getCursor(toolTable);
+ if (cursor.moveToFirst()) {
+ do {
+ int id = cursor.getInt(0);
+ boolean bought = cursor.getInt(1) == 1;
+ int toolLevel = cursor.getInt(2);
+
+ ToolType tool = ToolType.values()[id];
+ tool.setBought(bought);
+ tool.setCurrentUpgradeLevel(toolLevel);
+ } while (cursor.moveToNext());
+ }
+ cursor.close();
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/sqlDatabase/SQLTable.java b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/SQLTable.java
new file mode 100644
index 0000000..7054754
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/SQLTable.java
@@ -0,0 +1,38 @@
+package de.frajul.endlessroll.sqlDatabase;
+
+public class SQLTable {
+
+ private final String name;
+ private final String columnsAsText;
+
+ public SQLTable(String name, SQLTableColumn... columns) {
+ this.name = name;
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < columns.length; i++) {
+ builder.append(columns[i].toString());
+ if (i != columns.length - 1)
+ builder.append(", ");
+ }
+ columnsAsText = builder.toString();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getSqlCreateTable() {
+ return "CREATE TABLE " + name + "(" + columnsAsText + ");";
+ }
+
+ public String getSqlDeleteTable() {
+ return "DROP TABLE IF EXISTS " + name;
+ }
+
+ public String getSqlClearTable() {
+ return "DELETE FROM " + name;
+ }
+
+ public String getSqlGetCursor() {
+ return "SELECT * FROM " + name;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/sqlDatabase/SQLTableColumn.java b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/SQLTableColumn.java
new file mode 100644
index 0000000..a878e96
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/SQLTableColumn.java
@@ -0,0 +1,48 @@
+package de.frajul.endlessroll.sqlDatabase;
+
+public class SQLTableColumn {
+
+ public enum ColumnType {
+ TEXT, INTEGER, REAL;
+ }
+
+ public enum ColumnExtra {
+ NONE(""), PRIMARY_KEY("PRIMARY KEY"), NOT_NULL("NOT NULL");
+
+ private String name;
+
+ ColumnExtra(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ private String title;
+ private ColumnType type;
+ private ColumnExtra extra;
+
+ public SQLTableColumn(String title, ColumnType type, ColumnExtra extra) {
+ this.title = title;
+ this.type = type;
+ this.extra = extra;
+ }
+
+ public SQLTableColumn(String title, ColumnType type) {
+ this.title = title;
+ this.type = type;
+ this.extra = ColumnExtra.NOT_NULL;
+ }
+
+ public String getKey() {
+ return title;
+ }
+
+ @Override
+ public String toString() {
+ return title + " " + type.toString() + " " + extra.toString();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/frajul/endlessroll/sqlDatabase/SQLTablePrimaryKeyColumn.java b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/SQLTablePrimaryKeyColumn.java
new file mode 100644
index 0000000..0c35792
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/sqlDatabase/SQLTablePrimaryKeyColumn.java
@@ -0,0 +1,21 @@
+package de.frajul.endlessroll.sqlDatabase;
+
+/**
+ * Created by Julian on 30.05.2016.
+ */
+public class SQLTablePrimaryKeyColumn extends SQLTableColumn {
+
+ private boolean autoincrement;
+
+ public SQLTablePrimaryKeyColumn(boolean autoincrement) {
+ super("_ID", ColumnType.INTEGER, ColumnExtra.PRIMARY_KEY);
+ this.autoincrement = autoincrement;
+ }
+
+ @Override
+ public String toString() {
+ if (autoincrement)
+ return super.toString() + " AUTOINCREMENT";
+ return super.toString();
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/user/LevelBounty.java b/app/src/main/java/de/frajul/endlessroll/user/LevelBounty.java
new file mode 100644
index 0000000..16beee0
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/user/LevelBounty.java
@@ -0,0 +1,63 @@
+package de.frajul.endlessroll.user;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.views.BountyMessage;
+
+/**
+ * Created by Julian on 26.01.2017.
+ */
+
+public class LevelBounty {
+
+ private int starCount = 0;
+ private int energyCount = 0;
+ private ToolType toolType;
+ private boolean toolSlot = false;
+
+ public LevelBounty(int starCount, int energyCount, ToolType toolType, boolean toolSlot) {
+ this.starCount = starCount;
+ this.energyCount = energyCount;
+ this.toolType = toolType;
+ this.toolSlot = toolSlot;
+ }
+
+ public List createBountyMessages(GameActivity gameActivity, BountyMessage.ScreenSwitchCaller caller) {
+ List list = new ArrayList();
+ if (starCount != 0)
+ list.add(new BountyMessage(gameActivity, BountyMessage.MessageType.STARS, starCount + "",
+ caller, R.drawable.currency_star));
+ if (energyCount != 0)
+ list.add(new BountyMessage(gameActivity, BountyMessage.MessageType.ENERGY, energyCount + "",
+ caller, R.drawable.currency_energy));
+ if (toolType != null)
+ list.add(new BountyMessage(gameActivity, BountyMessage.MessageType.TOOL, gameActivity.getString(toolType.getName()),
+ caller, toolType.getButtonDrawable()));
+ if (toolSlot)
+ list.add(new BountyMessage(gameActivity, BountyMessage.MessageType.TOOL_SLOT, null, caller,
+ R.drawable.tools_button_unlocked));
+ return list;
+ }
+
+ public ToolType getToolType() {
+ return toolType;
+ }
+
+ public boolean isToolSlot() {
+ return toolSlot;
+ }
+
+ public int getStarCount() {
+ return starCount;
+ }
+
+ public int getEnergyCount() {
+ return energyCount;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/user/LevelUpBounties.java b/app/src/main/java/de/frajul/endlessroll/user/LevelUpBounties.java
new file mode 100644
index 0000000..d118503
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/user/LevelUpBounties.java
@@ -0,0 +1,63 @@
+package de.frajul.endlessroll.user;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import de.frajul.endlessroll.entities.tools.ToolType;
+
+/**
+ * Created by Julian on 17.07.2016.
+ */
+public class LevelUpBounties extends HashMap {
+
+ private List unlockedTools = new ArrayList<>();
+ private int unlockedToolSlots;
+
+ public LevelUpBounties(int level) {
+ super.put(1, new LevelBounty(0, 0, ToolType.RAMP, true));
+ super.put(2, new LevelBounty(5, 0, ToolType.SPRING, false));
+ super.put(3, new LevelBounty(6, 0, ToolType.POWER_MUSHROOM, true));
+ super.put(4, new LevelBounty(7, 0, ToolType.BOMB, false));
+ super.put(5, new LevelBounty(8, 2, ToolType.MAGNET, false));
+ super.put(6, new LevelBounty(8, 1, null, true));
+ super.put(7, new LevelBounty(8, 1, ToolType.STASIS, false));
+ super.put(8, new LevelBounty(100, 100, null, false));
+ super.put(9, new LevelBounty(0, 0, null, true));
+ loadAllForLevel(level);
+ }
+
+ public void loadAllForLevel(int level) {
+ unlockedTools.clear();
+ unlockedToolSlots = 0;
+ for (int i = level; i > 0; i--)
+ loadBounty(super.get(i));
+ }
+
+ private void loadBounty(LevelBounty bounty) {
+ if (bounty != null) {
+ if (bounty.getToolType() != null)
+ unlockedTools.add(bounty.getToolType());
+ if (bounty.isToolSlot())
+ unlockedToolSlots++;
+ }
+ }
+
+ public int getLevelToolIsUnlocked(ToolType tool) {
+ for (int level : this.keySet()) {
+ LevelBounty bounty = this.get(level);
+ if (tool.equals(bounty.getToolType()))
+ return level;
+ }
+ return -1;
+ }
+
+ public boolean isToolLocked(ToolType tool) {
+ return !unlockedTools.contains(tool);
+ }
+
+ public boolean isToolSlotLocked(int index) {
+ return index >= unlockedToolSlots;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/user/ToolSlotSettings.java b/app/src/main/java/de/frajul/endlessroll/user/ToolSlotSettings.java
new file mode 100644
index 0000000..2a3901d
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/user/ToolSlotSettings.java
@@ -0,0 +1,49 @@
+package de.frajul.endlessroll.user;
+
+import de.frajul.endlessroll.entities.tools.ToolSlot;
+import de.frajul.endlessroll.entities.tools.ToolType;
+
+import java.util.ArrayList;
+
+/**
+ * Created by Julian on 15.07.2016.
+ */
+public class ToolSlotSettings extends ArrayList {
+
+ public ToolSlotSettings(String slot1, String slot2, String slot3, String slot4, int slotsLocked) throws Exception {
+ super.add(new ToolSlot(slot1.equals("null") ? null : ToolType.valueOf(slot1), false));
+ super.add(new ToolSlot(slot2.equals("null") ? null : ToolType.valueOf(slot2), slotsLocked >= 3));
+ super.add(new ToolSlot(slot3.equals("null") ? null : ToolType.valueOf(slot3), slotsLocked >= 2));
+ super.add(new ToolSlot(slot4.equals("null") ? null : ToolType.valueOf(slot4), slotsLocked >= 1));
+ }
+
+ public void reset() {
+ super.set(0, new ToolSlot(ToolType.RAMP, false));
+ super.set(1, new ToolSlot(null, true));
+ super.set(2, new ToolSlot(null, true));
+ super.set(3, new ToolSlot(null, true));
+ }
+
+ public void changeToolSlotType(int slot, ToolType newType) {
+ for (ToolSlot toolSlot : this) {
+ if (super.indexOf(toolSlot) == slot)
+ toolSlot.setToolType(newType);
+ else if (toolSlot.getToolType() == newType)
+ toolSlot.setToolType(null);
+ }
+ }
+
+ public void unlockSlotsIfLevelReached(LevelUpBounties levelUpBounties) {
+ for (int i = 0; i < 4; i++)
+ super.get(i).setLocked(levelUpBounties.isToolSlotLocked(i));
+ }
+
+ public int getLockedSlotCount() {
+ int count = 0;
+ for (ToolSlot slot : this)
+ if(slot.isLocked())
+ count ++;
+ return count;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/user/User.java b/app/src/main/java/de/frajul/endlessroll/user/User.java
new file mode 100644
index 0000000..f3d4a40
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/user/User.java
@@ -0,0 +1,146 @@
+package de.frajul.endlessroll.user;
+
+import de.frajul.endlessroll.entities.shapes.PlayerShape;
+
+/**
+ * Created by Julian on 10.07.2016.
+ */
+public class User {
+
+ public interface LvUpListener {
+ public void onLvUp(int level);
+ }
+
+ private final int LV_DONE_EP = 20;
+ private final int STAR_EP = 2;
+ private final int ENERGY_EP = 10;
+ //MAX EP IN ONE LEVEL: 36 (20 + 3*2 + 10)
+
+ private LvUpListener lvUpListener;
+ private int ep;
+ private int level;
+ private int starCount;
+ private int energyCount;
+ private ToolSlotSettings toolSlotSettings;
+ private PlayerShape currentPlayerShape;
+
+ private LevelUpBounties levelUpBounties;
+
+ private int inLevelCollectedStars;
+ private int inLevelCollectedEnergy;
+
+ public User(LvUpListener lvUpListener, int ep, int level, int starCount, int energyCount, ToolSlotSettings toolSlotSettings, PlayerShape playerShape) {
+ this.lvUpListener = lvUpListener;
+ this.ep = ep;
+ this.level = level;
+ this.starCount = starCount;
+ this.energyCount = energyCount;
+ this.toolSlotSettings = toolSlotSettings;
+ this.currentPlayerShape = playerShape;
+
+ levelUpBounties = new LevelUpBounties(level);
+ }
+
+ public void clearData() {
+ ep = 0;
+ level = 1;
+ starCount = 0;
+ energyCount = 0;
+ toolSlotSettings.reset();
+ currentPlayerShape = PlayerShape.BALL;
+ }
+
+ public void onStarCollected() {
+ increaseStarCount(1, true);
+ }
+
+ public void increaseStarCount(int starCount, boolean gainEp) {
+ this.starCount += starCount;
+ if(gainEp)
+ gainEp(STAR_EP * starCount);
+ }
+
+ public void onEnergyCollected() {
+ increaseEnergyCount(1, true);
+ }
+
+ public void increaseEnergyCount(int energyCount, boolean gainEp) {
+ this.energyCount += energyCount;
+ if(gainEp)
+ gainEp(ENERGY_EP * energyCount);
+ }
+
+ public void gainLvFinishedEp() {
+ gainEp(LV_DONE_EP);
+ }
+
+ public void gainEp(int amount) {
+ ep += amount;
+ if (ep >= 100) {
+ ep -= 100;
+ levelUp();
+ }
+ }
+
+ private void levelUp() {
+ level++;
+ levelUpBounties.loadAllForLevel(level);
+
+ toolSlotSettings.unlockSlotsIfLevelReached(levelUpBounties);
+ LevelBounty bounty = levelUpBounties.get(level);
+ if (bounty != null) {
+ increaseStarCount(bounty.getStarCount(), false);
+ increaseEnergyCount(bounty.getEnergyCount(), false);
+ }
+ lvUpListener.onLvUp(level);
+ }
+
+ public int getEp() {
+ return ep;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ public int getStarCount() {
+ return starCount;
+ }
+
+ public int getEnergyCount() {
+ return energyCount;
+ }
+
+ public ToolSlotSettings getToolSlotSettings() {
+ return toolSlotSettings;
+ }
+
+ public PlayerShape getCurrentPlayerShape() {
+ return currentPlayerShape;
+ }
+
+ public void setCurrentPlayerShape(PlayerShape currentPlayerShape) {
+ this.currentPlayerShape = currentPlayerShape;
+ }
+
+ public LevelUpBounties getLevelUpBounties() {
+ return levelUpBounties;
+ }
+
+ public int getInLevelCollectedEnergy() {
+ return inLevelCollectedEnergy;
+ }
+
+ public int getInLevelCollectedStars() {
+ return inLevelCollectedStars;
+ }
+
+ //CHEAT
+ public void setLevel(int level) {
+ this.level = level;
+ }
+
+ public void setStarCount(int starCount) {
+ this.starCount = starCount;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/BountyMessage.java b/app/src/main/java/de/frajul/endlessroll/views/BountyMessage.java
new file mode 100644
index 0000000..345b9f3
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/BountyMessage.java
@@ -0,0 +1,89 @@
+package de.frajul.endlessroll.views;
+
+import android.content.Context;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.screens.Screen;
+
+/**
+ * Created by Julian on 15.07.2016.
+ */
+public class BountyMessage implements View.OnClickListener {
+
+ public interface ScreenSwitchCaller {
+ public void switchScreen(Screen.ScreenType screenType);
+ }
+
+ public enum MessageType {
+ STARS(R.string.bounty_message_resource_format_s, Screen.ScreenType.NONE),
+ ENERGY(R.string.bounty_message_resource_format_s, Screen.ScreenType.NONE),
+ TOOL(R.string.bounty_message_tool_format_s, Screen.ScreenType.TOOL_SHOP),
+ TOOL_SLOT(R.string.bounty_message_tool_slot, Screen.ScreenType.TOOL_SHOP),
+ SHAPE_UNLOCKED(R.string.bounty_message_shape_unlocked, Screen.ScreenType.SHAPE_SHOP);
+
+ @StringRes
+ private int textId;
+ private Screen.ScreenType goalScreenType;
+
+ MessageType(@StringRes int textId, Screen.ScreenType goalScreenType) {
+ this.textId = textId;
+ this.goalScreenType = goalScreenType;
+ }
+
+ public String formatText(Context context, @Nullable String textArgs) {
+ String text = context.getString(textId);
+ if (textArgs == null)
+ return text;
+ return String.format(text, textArgs);
+ }
+
+
+ public Screen.ScreenType getGoalScreenType() {
+ return goalScreenType;
+ }
+
+ }
+
+ private LinearLayout layout;
+ private ImageView image;
+ private TextView text;
+
+ private MessageType messageType;
+ private ScreenSwitchCaller screenSwitchCaller;
+
+ public BountyMessage(GameActivity gameActivity, MessageType messageType, @Nullable String textArgs, ScreenSwitchCaller screenSwitchCaller, @DrawableRes int drawableId) {
+ findViews(gameActivity);
+ this.image.setImageResource(drawableId);
+ this.text.setText(messageType.formatText(gameActivity, textArgs));
+ this.text.setTypeface(gameActivity.getTypeface());
+ this.messageType = messageType;
+ this.screenSwitchCaller = screenSwitchCaller;
+ }
+
+ private void findViews(Context context) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ layout = (LinearLayout) inflater.inflate(R.layout.unlock_message, null);
+ layout.setOnClickListener(this);
+ image = (ImageView) layout.findViewById(R.id.unlockmessage_toolimage);
+ text = (TextView) layout.findViewById(R.id.unlockmessage_message);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (messageType.getGoalScreenType() != Screen.ScreenType.NONE)
+ screenSwitchCaller.switchScreen(messageType.getGoalScreenType());
+ }
+
+ public LinearLayout getLayout() {
+ return layout;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/Countdown.java b/app/src/main/java/de/frajul/endlessroll/views/Countdown.java
new file mode 100644
index 0000000..572176d
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/Countdown.java
@@ -0,0 +1,69 @@
+package de.frajul.endlessroll.views;
+
+import android.graphics.Typeface;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.widget.TextView;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.main.game.Game;
+
+/**
+ * Created by Julian on 31.07.2016.
+ */
+public class Countdown implements Animation.AnimationListener {
+
+ private Game game;
+ private AnimationSet animations;
+ private TextView textView;
+
+ private boolean firstHalfRepeated = true;
+ private int repeatCount = 0;
+
+ public Countdown(Game game, Typeface typeface, TextView textView) {
+ this.game = game;
+ this.textView = textView;
+ this.textView.setTypeface(typeface);
+ animations = (AnimationSet) AnimationUtils.loadAnimation(game.getContext(), R.anim.countdown);
+ for (Animation animation : animations.getAnimations())
+ animation.setAnimationListener(this);
+ }
+
+ public void start() {
+ reset();
+ textView.startAnimation(animations);
+ }
+
+ public void stop() {
+ textView.clearAnimation();
+ }
+
+ private void reset() {
+ repeatCount = 0;
+ textView.setText("3");
+ textView.setTextColor(game.getContext().getResources().getColor(R.color.countdown3));
+ }
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ game.countdownFinished();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ if (!firstHalfRepeated)
+ repeatCount++;
+ firstHalfRepeated = !firstHalfRepeated;
+ textView.setText((3 - repeatCount) + "");
+ if (repeatCount == 1) {
+ textView.setTextColor(game.getContext().getResources().getColor(R.color.countdown2));
+ } else if (repeatCount == 2) {
+ textView.setTextColor(game.getContext().getResources().getColor(R.color.countdown1));
+ }
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/GameOverMessage.java b/app/src/main/java/de/frajul/endlessroll/views/GameOverMessage.java
new file mode 100644
index 0000000..4e84dec
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/GameOverMessage.java
@@ -0,0 +1,68 @@
+package de.frajul.endlessroll.views;
+
+import android.graphics.Typeface;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.TextView;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.game.Game;
+import de.frajul.endlessroll.main.screens.Screen;
+
+/**
+ * Created by Julian on 09.07.2016.
+ */
+public class GameOverMessage implements View.OnClickListener {
+
+ private Animation fadeIn;
+ private Game game;
+
+ private View layout;
+ private TopBar topBar;
+ private Button tryAgain;
+ private Button toMenu;
+
+ public GameOverMessage(Game game, GameActivity gameActivity, View layout) {
+ this.game = game;
+ this.layout = layout;
+ layout.setVisibility(View.GONE);
+ Typeface typeface = gameActivity.getTypeface();
+ fadeIn = AnimationUtils.loadAnimation(gameActivity, R.anim.fade_in);
+ topBar = new TopBar(gameActivity, Screen.ScreenType.GAME,
+ layout.findViewById(R.id.game_over_message_topbar));
+ topBar.setShopsEnabled(true);
+ TextView title = (TextView) layout.findViewById(R.id.game_over_message_title);
+ title.setTypeface(typeface);
+ tryAgain = (Button) layout.findViewById(R.id.game_over_message_try_again);
+ tryAgain.setTypeface(typeface);
+ tryAgain.setOnClickListener(this);
+ toMenu = (Button) layout.findViewById(R.id.game_over_message_to_menu);
+ toMenu.setTypeface(typeface);
+ toMenu.setOnClickListener(this);
+ }
+
+ public void fadeIn() {
+ topBar.update();
+ layout.startAnimation(fadeIn);
+ layout.setVisibility(View.VISIBLE);
+ }
+
+ private void hide() {
+ layout.clearAnimation();
+ layout.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.equals(tryAgain)) {
+ hide();
+ game.restartLevel();
+ } else if (v.equals(toMenu)) {
+ hide();
+ game.toLevelsScreen();
+ }
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/LevelButton.java b/app/src/main/java/de/frajul/endlessroll/views/LevelButton.java
new file mode 100644
index 0000000..1a8adb5
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/LevelButton.java
@@ -0,0 +1,84 @@
+package de.frajul.endlessroll.views;
+
+import android.support.annotation.LayoutRes;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.levels.Level;
+import de.frajul.endlessroll.main.GameActivity;
+
+/**
+ * Created by Julian on 23.04.2016.
+ */
+public class LevelButton implements View.OnClickListener {
+
+ private GameActivity gameActivity;
+ private LevelButtonOnClickListener clickListener;
+ private Level level;
+
+ private View layout;
+ private TextView text;
+ private ImageView star1;
+ private ImageView star2;
+ private ImageView star3;
+ private ImageView energy;
+ private ImageView lockImage;
+
+ public LevelButton(GameActivity gameActivity, LevelButtonOnClickListener clickListener, @LayoutRes int layoutId) {
+ this.gameActivity = gameActivity;
+ this.clickListener = clickListener;
+ LayoutInflater inflater = LayoutInflater.from(gameActivity);
+ layout = inflater.inflate(layoutId, null);
+ layout.setOnClickListener(this);
+ findViews(layout);
+ }
+
+ private void findViews(View layout) {
+ text = (TextView) layout.findViewById(R.id.levelbutton_textview);
+ text.setTypeface(gameActivity.getTypeface());
+ star1 = (ImageView) layout.findViewById(R.id.levelbutton_star1);
+ star2 = (ImageView) layout.findViewById(R.id.levelbutton_star2);
+ star3 = (ImageView) layout.findViewById(R.id.levelbutton_star3);
+ energy = (ImageView) layout.findViewById(R.id.levelbutton_energy);
+ lockImage = (ImageView) layout.findViewById(R.id.levelbutton_lock);
+ }
+
+ public void init(Level level) {
+ this.level = level;
+ setLockVisible(level.isLocked());
+ text.setText(gameActivity.getString(R.string.level_button_format_d, level.getId()));
+ showCollectedCurrency(level.getCollectedStars(), level.isEnergyCollected());
+ }
+
+ private void setLockVisible(boolean locked) {
+ int visibility = locked ? View.VISIBLE : View.GONE;
+ lockImage.setVisibility(visibility);
+ }
+
+ private void showCollectedCurrency(boolean[] stars, boolean energy) {
+ if (stars[0])
+ this.star1.setImageResource(R.drawable.currency_star);
+ if (stars[1])
+ this.star2.setImageResource(R.drawable.currency_star);
+ if (stars[2])
+ this.star3.setImageResource(R.drawable.currency_star);
+ if (energy)
+ this.energy.setImageResource(R.drawable.currency_energy);
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public View getView(){
+ return layout;
+ }
+
+ @Override
+ public void onClick(View v) {
+ clickListener.onClick(this);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/LevelupMessage.java b/app/src/main/java/de/frajul/endlessroll/views/LevelupMessage.java
new file mode 100644
index 0000000..7fd32a0
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/LevelupMessage.java
@@ -0,0 +1,80 @@
+package de.frajul.endlessroll.views;
+
+import android.graphics.Typeface;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.entities.tileLists.TileList;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.screens.Screen;
+import de.frajul.endlessroll.user.LevelBounty;
+import de.frajul.endlessroll.user.User;
+
+/**
+ * Created by Julian on 15.07.2016.
+ */
+public class LevelupMessage implements View.OnClickListener, BountyMessage.ScreenSwitchCaller {
+
+ private GameActivity gameActivity;
+ private FrameLayout layout;
+
+ private TextView levelView;
+ private TextView text;
+ private LinearLayout unlockMessages;
+
+ public LevelupMessage(GameActivity gameActivity) {
+ this.gameActivity = gameActivity;
+
+ Typeface typeface = gameActivity.getTypeface();
+ LayoutInflater inflater = LayoutInflater.from(gameActivity);
+ layout = (FrameLayout) inflater.inflate(R.layout.levelup_message, null);
+ layout.setOnClickListener(this);
+ layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ layout.setVisibility(View.GONE);
+ levelView = (TextView) layout.findViewById(R.id.levelup_level);
+ levelView.setTypeface(typeface);
+ text = (TextView) layout.findViewById(R.id.levelup_text);
+ text.setTypeface(typeface);
+ unlockMessages = (LinearLayout) layout.findViewById(R.id.levelup_unlocklist);
+ }
+
+ public void show(int level) {
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ params.setMargins(0, 5, 0, 5);
+
+ levelView.setText(level + "");
+
+ LevelBounty bounty = gameActivity.getUser().getLevelUpBounties().get(level);
+ if (bounty != null)
+ for (BountyMessage message : bounty.createBountyMessages(gameActivity, this))
+ unlockMessages.addView(message.getLayout(), params);
+
+ layout.setVisibility(View.VISIBLE);
+ }
+
+ private void hide() {
+ layout.setVisibility(View.GONE);
+ unlockMessages.removeAllViews();
+ }
+
+ public FrameLayout getLayout() {
+ return layout;
+ }
+
+ @Override
+ public void onClick(View v) {
+ hide();
+ }
+
+
+ @Override
+ public void switchScreen(Screen.ScreenType screenType) {
+ gameActivity.flipToScreen(screenType);
+ hide();
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/ShortMenu.java b/app/src/main/java/de/frajul/endlessroll/views/ShortMenu.java
new file mode 100644
index 0000000..bcd684c
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/ShortMenu.java
@@ -0,0 +1,93 @@
+package de.frajul.endlessroll.views;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.util.Random;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.game.Game;
+import de.frajul.endlessroll.main.screens.Screen;
+
+/**
+ * Created by Julian on 23.01.2016.
+ */
+public class ShortMenu implements View.OnClickListener {
+
+ private Game game;
+ private Random random;
+ private Animation slideLeft;
+ private Animation slideRight;
+ private Animation slideTop;
+
+ private View layout;
+ private TopBar topBar;
+ private TextView continueView;
+ private TextView restartView;
+ private TextView exitView;
+
+ public ShortMenu(final Game game, GameActivity gameActivity, View layout) {
+ this.game = game;
+ this.layout = layout;
+ Typeface typeface = gameActivity.getTypeface();
+ topBar = new TopBar(gameActivity, Screen.ScreenType.GAME,
+ layout.findViewById(R.id.shortmenu_topbar));
+ continueView = (TextView) layout.findViewById(R.id.shortmenu_continue);
+ continueView.setTypeface(typeface);
+ continueView.setOnClickListener(this);
+ restartView = (TextView) layout.findViewById(R.id.shortmenu_restart);
+ restartView.setTypeface(typeface);
+ restartView.setOnClickListener(this);
+ exitView = (TextView) layout.findViewById(R.id.shortmenu_exit);
+ exitView.setTypeface(typeface);
+ exitView.setOnClickListener(this);
+
+ random = new Random();
+ Context context = game.getContext();
+ slideLeft = AnimationUtils.loadAnimation(context, R.anim.slide_in_left);
+ slideRight = AnimationUtils.loadAnimation(context, R.anim.slide_in_right);
+ slideTop = AnimationUtils.loadAnimation(context, R.anim.slide_in_top);
+ }
+
+ public void startAnims() {
+ startRandomAnimation(continueView);
+ startRandomAnimation(restartView);
+ startRandomAnimation(exitView);
+ topBar.startAnimation(slideTop);
+ }
+
+ public void setVisible(boolean visible) {
+ layout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ if (visible)
+ topBar.update();
+ }
+
+ private void startRandomAnimation(View view) {
+ float r = random.nextFloat();
+ if (r >= 0.5)
+ view.startAnimation(slideRight);
+ else
+ view.startAnimation(slideLeft);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.equals(continueView)) {
+ setVisible(false);
+ game.continueGame();
+ } else if (v.equals(restartView)) {
+ setVisible(false);
+ game.restartLevel();
+ } else if (v.equals(exitView)) {
+ setVisible(false);
+ game.toLevelsScreen();
+ }
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/ToolButton.java b/app/src/main/java/de/frajul/endlessroll/views/ToolButton.java
new file mode 100644
index 0000000..1eecdac
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/ToolButton.java
@@ -0,0 +1,126 @@
+package de.frajul.endlessroll.views;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.entities.tools.ToolSlot;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.entities.tools.ToolUpgradeType;
+
+/**
+ * Created by Julian on 15.01.2016.
+ */
+public class ToolButton {
+
+ private boolean locked;
+ private ToolType toolType;
+ private Context context;
+ private float progress = 100;
+ private boolean active = false;
+
+ private RelativeLayout layout;
+ private ProgressBar progressBar;
+ private ImageView backgroundView;
+ private ImageView animationView;
+ private Animation scaleAnimation;
+
+ public ToolButton(ToolSlot slot, Context context, RelativeLayout layout) {
+ this.context = context;
+ this.layout = layout;
+ progressBar = (ProgressBar) layout.findViewById(R.id.tool_button_progress_bar);
+
+ scaleAnimation = AnimationUtils.loadAnimation(context, R.anim.scale_up);
+ changeToolSlot(slot);
+ }
+
+ public void changeToolSlot(ToolSlot toolSlot) {
+ this.toolType = toolSlot.getToolType();
+ this.locked = toolSlot.isLocked();
+ backgroundView = createImageView(toolType, R.id.tool_button_background_layer);
+ animationView = createImageView(toolType, R.id.tool_button_animation_layer);
+ }
+
+ private ImageView createImageView(ToolType type, int id) {
+ ImageView view = (ImageView) layout.findViewById(id);
+ if (locked)
+ view.setImageResource(R.drawable.tools_button_locked);
+ else if (type != null)
+ view.setImageResource(type.getButtonDrawable());
+ else
+ view.setImageResource(R.drawable.tools_button_empty);
+ view.setBackgroundDrawable(createColoredBackground());
+ return view;
+ }
+
+ private Drawable createColoredBackground() {
+ GradientDrawable gd = new GradientDrawable();
+ gd.setColor(context.getResources().getColor(R.color.toolbuttonInactiveNotReady));
+ if (locked || toolType == null) {
+ gd.setColor(context.getResources().getColor(R.color.toolbuttonLocked));
+ }
+ gd.setCornerRadius(15);
+ return gd;
+ }
+
+ public void update(float frameTime) {
+ if (progress != 100) {
+ progress += (frameTime) / (toolType
+ .getCurrentUpgradeValue(ToolUpgradeType.COOLDOWN) / 100);
+ progress = Math.min(progress, 100);
+ if (progress == 100)
+ animationView.startAnimation(scaleAnimation);
+ }
+ progressBar.setProgress(100 - (int) progress);
+ if (!locked && toolType != null) {
+ if (active) {
+ if (progress == 100)
+ setColor(R.color.toolbuttonActiveReady);
+ else
+ setColor(R.color.toolbuttonActiveNotReady);
+ } else {
+ if (progress == 100)
+ setColor(R.color.toolbuttonInactiveReady);
+ else
+ setColor(R.color.toolbuttonInactiveNotReady);
+ }
+ }
+ }
+
+ private void setColor(int id) {
+ GradientDrawable gd = (GradientDrawable) backgroundView.getBackground();
+ GradientDrawable animGd = (GradientDrawable) animationView.getBackground();
+ gd.setColor(context.getResources().getColor(id));
+ animGd.setColor(context.getResources().getColor(id));
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setProgress(int progress) {
+ this.progress = progress;
+ }
+
+ public boolean finishedLoading() {
+ return progress == 100;
+ }
+
+ public ToolType getToolType() {
+ return toolType;
+ }
+
+ public boolean isLocked() {
+ return locked;
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/ToolButtonBar.java b/app/src/main/java/de/frajul/endlessroll/views/ToolButtonBar.java
new file mode 100644
index 0000000..75bbe4a
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/ToolButtonBar.java
@@ -0,0 +1,149 @@
+package de.frajul.endlessroll.views;
+
+import android.content.Context;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.main.GameLog;
+import de.frajul.endlessroll.main.game.Game;
+import de.frajul.endlessroll.main.game.GameState;
+import de.frajul.endlessroll.user.ToolSlotSettings;
+
+/**
+ * Created by Julian on 16.01.2016.
+ */
+public class ToolButtonBar implements View.OnClickListener, Animation.AnimationListener {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+
+ private Animation fadeIn, fadeOut;
+ private Game game;
+ private List buttons = new ArrayList<>(4);
+ private RelativeLayout button1;
+ private RelativeLayout button2;
+ private RelativeLayout button3;
+ private RelativeLayout button4;
+
+
+ public ToolButtonBar(Game game, ToolSlotSettings toolSlotSettings, LinearLayout layout1) {
+ this.game = game;
+ Context context = game.getContext();
+ fadeIn = AnimationUtils.loadAnimation(game.getContext(), R.anim.fade_in);
+ fadeIn.setAnimationListener(this);
+ fadeOut = AnimationUtils.loadAnimation(game.getContext(), R.anim.fade_out);
+
+ button1 = (RelativeLayout) layout1.findViewById(R.id.toolbutton_1);
+ button1.setOnClickListener(this);
+ button2 = (RelativeLayout) layout1.findViewById(R.id.toolbutton_2);
+ button2.setOnClickListener(this);
+ button3 = (RelativeLayout) layout1.findViewById(R.id.toolbutton_3);
+ button3.setOnClickListener(this);
+ button4 = (RelativeLayout) layout1.findViewById(R.id.toolbutton_4);
+ button4.setOnClickListener(this);
+ buttons.add(new ToolButton(toolSlotSettings.get(0), context, button1));
+ buttons.add(new ToolButton(toolSlotSettings.get(1), context, button2));
+ buttons.add(new ToolButton(toolSlotSettings.get(2), context, button3));
+ buttons.add(new ToolButton(toolSlotSettings.get(3), context, button4));
+ }
+
+ public void setTopPrimary() {
+ button1.startAnimation(fadeIn);
+ button2.startAnimation(fadeIn);
+ button3.startAnimation(fadeIn);
+ button4.startAnimation(fadeIn);
+ }
+
+ public void setBottomPrimary() {
+ button1.startAnimation(fadeOut);
+ button2.startAnimation(fadeOut);
+ button3.startAnimation(fadeOut);
+ button4.startAnimation(fadeOut);
+ }
+
+ public void changeToolButtonTypes(ToolSlotSettings toolSlotSettings) {
+ for (int i = 0; i < 4; i++) {
+ buttons.get(i).changeToolSlot(toolSlotSettings.get(i));
+ }
+ }
+
+ public void reset(ToolSlotSettings toolSlotSettings) {
+ GameLog.i("Reset toolbuttonBar");
+ changeToolButtonTypes(toolSlotSettings);
+ for (int i = 0; i < 4; i++) {
+ if (toolSlotSettings.get(i).getToolType() != null) {
+ setActive(i);
+ break;
+ }
+ }
+ for (ToolButton button : buttons)
+ button.setProgress(100);
+ }
+
+ public void update(float frameTime) {
+ for (ToolButton button : buttons)
+ button.update(frameTime);
+ }
+
+ public void setActive(ToolType activeType) {
+ for (ToolButton button : buttons)
+ button.setActive(button.getToolType() == activeType);
+ }
+
+ private void setActive(int index) {
+ for (ToolButton button : buttons)
+ button.setActive(buttons.indexOf(button) == index);
+ }
+
+ public ToolButton getByToolType(ToolType type) {
+ for (ToolButton button : buttons)
+ if (button.getToolType() == type)
+ return button;
+ return null;
+ }
+
+ public ToolButton getActiveButton() {
+ for (ToolButton button : buttons)
+ if (button.isActive())
+ return button;
+ return null;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (game.getGameState() == GameState.RUNNING) {
+ ToolType clickedType = null;
+ if (v.equals(button1) && !buttons.get(0).isLocked()) {
+ clickedType = buttons.get(0).getToolType();
+ } else if (v.equals(button2) && !buttons.get(1).isLocked()) {
+ clickedType = buttons.get(1).getToolType();
+ } else if (v.equals(button3) && !buttons.get(2).isLocked()) {
+ clickedType = buttons.get(2).getToolType();
+ } else if (v.equals(button4) && !buttons.get(3).isLocked()) {
+ clickedType = buttons.get(3).getToolType();
+ }
+ if (clickedType != null) {
+ game.setCurrentTool(clickedType);
+ setActive(clickedType);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/ToolOfferSlot.java b/app/src/main/java/de/frajul/endlessroll/views/ToolOfferSlot.java
new file mode 100644
index 0000000..55ce70c
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/ToolOfferSlot.java
@@ -0,0 +1,101 @@
+package de.frajul.endlessroll.views;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.entities.tools.ToolType;
+import de.frajul.endlessroll.main.screens.ToolShopScreen;
+
+/**
+ * Created by Julian on 16.07.2016.
+ */
+public class ToolOfferSlot implements View.OnClickListener {
+
+ private ToolShopScreen toolShopScreen;
+ private ToolType toolType;
+ private boolean locked;
+ private boolean selected;
+
+ private int colorDisabled;
+ private int colorDisabledSelected;
+ private int colorEnabled;
+ private int colorEnabledSelected;
+
+ private LinearLayout layout;
+ private TextView title;
+ private ImageView image;
+
+ public ToolOfferSlot(ToolShopScreen toolShopScreen, Context context, Typeface typeface, ToolType toolType) {
+ this.toolShopScreen = toolShopScreen;
+ this.toolType = toolType;
+ LayoutInflater inflater = LayoutInflater.from(context);
+ layout = (LinearLayout) inflater.inflate(R.layout.tool_offer_slot, null);
+ layout.setOnClickListener(this);
+ FrameLayout slotLayout = (FrameLayout) layout.findViewById(R.id.toolofferslot_slot);
+ title = (TextView) layout.findViewById(R.id.toolofferslot_title);
+ title.setTypeface(typeface);
+ image = (ImageView) slotLayout.findViewById(R.id.toolslot_image);
+ image.setBackgroundDrawable(createImageBackground());
+
+ colorDisabled = context.getResources().getColor(R.color.toolslotDisabled);
+ colorDisabledSelected = context.getResources().getColor(R.color.toolslotDisabledSelected);
+ colorEnabled = context.getResources().getColor(R.color.toolslotEnabled);
+ colorEnabledSelected = context.getResources().getColor(R.color.toolslotEnabledSelected);
+ }
+
+ private Drawable createImageBackground() {
+ GradientDrawable gd = new GradientDrawable();
+ gd.setCornerRadius(10);
+ return gd;
+ }
+
+ public void setLocked(boolean locked) {
+ this.locked = locked;
+ if (locked) {
+ title.setText(R.string.tool_name_locked);
+ image.setImageResource(R.drawable.tools_button_locked);
+ } else {
+ title.setText(toolType.getName());
+ image.setImageResource(toolType.getButtonDrawable());
+ }
+ }
+
+ public void updateBackgroundColor() {
+ GradientDrawable gd = (GradientDrawable) image.getBackground();
+ if (toolType.isBought())
+ gd.setColor(selected ? colorEnabledSelected : colorEnabled);
+ else
+ gd.setColor(selected ? colorDisabledSelected : colorDisabled);
+ }
+
+ public void setSelected(boolean selected) {
+ this.selected = selected;
+ updateBackgroundColor();
+ }
+
+ public boolean isLocked() {
+ return locked;
+ }
+
+ public ToolType getToolType() {
+ return toolType;
+ }
+
+ public LinearLayout getLayout() {
+ return layout;
+ }
+
+ @Override
+ public void onClick(View v) {
+ toolShopScreen.onToolOfferSlotSelected(this);
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/TopBar.java b/app/src/main/java/de/frajul/endlessroll/views/TopBar.java
new file mode 100644
index 0000000..1ef6ea6
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/TopBar.java
@@ -0,0 +1,121 @@
+package de.frajul.endlessroll.views;
+
+import android.graphics.Typeface;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.screens.Screen;
+import de.frajul.endlessroll.main.tutorial.ToolShopTutorial;
+import de.frajul.endlessroll.user.User;
+
+/**
+ * Created by Julian on 08.07.2016.
+ */
+public class TopBar implements View.OnClickListener {
+
+ private GameActivity gameActivity;
+ private View layout;
+
+ private Animation starDecreaseAnimation;
+ private Animation energyDecreaseAnimation;
+ private Animation toolShopPulse;
+
+ private TextView levelDisplay;
+ private ProgressBar levelProgress;
+ private TextView starCount;
+ private TextView energyCount;
+ private Button settingsButton;
+ private Button toolshopButton;
+ private Button shapeshopButton;
+ private TextView starCountDecrease;
+ private TextView energyCountDecrease;
+
+ public TopBar(GameActivity gameActivity, Screen.ScreenType parent, View layout) {
+ this.gameActivity = gameActivity;
+ this.layout = layout;
+
+ starDecreaseAnimation = AnimationUtils.loadAnimation(gameActivity, R.anim.decrease);
+ energyDecreaseAnimation = AnimationUtils.loadAnimation(gameActivity, R.anim.decrease);
+ toolShopPulse = AnimationUtils.loadAnimation(gameActivity, R.anim.pulse);
+
+ Typeface typeface = gameActivity.getTypeface();
+ levelDisplay = (TextView) layout.findViewById(R.id.topbar_leveldisplay);
+ levelDisplay.setTypeface(typeface);
+ levelProgress = (ProgressBar) layout.findViewById(R.id.topbar_levelprogress);
+ starCount = (TextView) layout.findViewById(R.id.topbar_starcount);
+ starCount.setTypeface(typeface);
+ energyCount = (TextView) layout.findViewById(R.id.topbar_energycount);
+ energyCount.setTypeface(typeface);
+ settingsButton = (Button) layout.findViewById(R.id.topbar_settings);
+ settingsButton.setOnClickListener(this);
+ toolshopButton = (Button) layout.findViewById(R.id.topbar_toolshop);
+ toolshopButton.setOnClickListener(this);
+ shapeshopButton = (Button) layout.findViewById(R.id.topbar_shapeshop);
+ shapeshopButton.setOnClickListener(this);
+ if (parent == Screen.ScreenType.TOOL_SHOP || parent == Screen.ScreenType.SHAPE_SHOP || parent == Screen.ScreenType.SETTINGS) {
+ toolshopButton.setEnabled(false);
+ shapeshopButton.setEnabled(false);
+ settingsButton.setEnabled(false);
+ }
+ if (parent == Screen.ScreenType.GAME) {
+ toolshopButton.setEnabled(false);
+ shapeshopButton.setEnabled(false);
+ }
+ starCountDecrease = (TextView) layout.findViewById(R.id.topbar_starcount_decrease);
+ starCountDecrease.setTypeface(typeface);
+ energyCountDecrease = (TextView) layout.findViewById(R.id.topbar_energycount_decrease);
+ energyCountDecrease.setTypeface(typeface);
+ }
+
+ public void setShopsEnabled(boolean enabled) {
+ toolshopButton.setEnabled(enabled);
+ shapeshopButton.setEnabled(enabled);
+ }
+
+ public void startAnimation(Animation animation) {
+ layout.startAnimation(animation);
+ }
+
+ public void update() {
+ toolshopButton.clearAnimation();
+
+ User user = gameActivity.getUser();
+ levelDisplay
+ .setText(gameActivity.getString(R.string.topbar_level_format_d, user.getLevel()));
+ levelProgress.setProgress(user.getEp());
+ starCount.setText(user.getStarCount() + "");
+ energyCount.setText(user.getEnergyCount() + "");
+
+ if (gameActivity.getTutorialManager().getToolShopTutorial()
+ .getState() == ToolShopTutorial.ToolShopTutorialState.TO_TOOLSHOP && toolshopButton
+ .isEnabled())
+ toolshopButton.startAnimation(toolShopPulse);
+ }
+
+ public void showStarcountDecrease(int decrease) {
+ starCountDecrease.setText(decrease + "");
+ starCountDecrease.startAnimation(starDecreaseAnimation);
+ }
+
+ public void showEnergycountDecrease(int decrease) {
+ energyCountDecrease.setText(decrease + "");
+ energyCountDecrease.startAnimation(energyDecreaseAnimation);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.equals(toolshopButton)) {
+ gameActivity.flipToScreen(Screen.ScreenType.TOOL_SHOP);
+ } else if (v.equals(shapeshopButton)) {
+ gameActivity.flipToScreen(Screen.ScreenType.SHAPE_SHOP);
+ } else if (v.equals(settingsButton)) {
+ gameActivity.flipToScreen(Screen.ScreenType.SETTINGS);
+ }
+ }
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/ViewManager.java b/app/src/main/java/de/frajul/endlessroll/views/ViewManager.java
new file mode 100644
index 0000000..e49edf4
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/ViewManager.java
@@ -0,0 +1,144 @@
+package de.frajul.endlessroll.views;
+
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.levels.Level;
+import de.frajul.endlessroll.levels.LevelPack;
+import de.frajul.endlessroll.main.GameActivity;
+import de.frajul.endlessroll.main.GameHandler;
+import de.frajul.endlessroll.main.game.Game;
+import de.frajul.endlessroll.main.game.Timer;
+import de.frajul.endlessroll.user.User;
+
+/**
+ * Created by Julian on 11.12.2015.
+ */
+public class ViewManager implements View.OnClickListener {
+
+ private GameHandler gameViewHandler;
+ private Game game;
+
+ private TextView fpsView;
+ private TextView playerProgress;
+ private TextView playerSpeed;
+ private ImageView pauseButton;
+ private GameOverMessage gameOverMessage;
+ private GoalMessage goalMessage;
+ public ToolButtonBar toolButtonBar;
+ public ShortMenu shortMenu;
+ private Countdown countdown;
+
+ private String fpsFormat, playerProgressFormat, playerSpeedFormat;
+
+ public ViewManager(final Game game, final GameHandler gameViewHandler, final GameActivity gameActivity) {
+ this.game = game;
+ this.gameViewHandler = gameViewHandler;
+
+ final RelativeLayout layout = gameViewHandler.getRootLayout();
+ gameViewHandler.startInUiThread(new Runnable() {
+ @Override
+ public void run() {
+ toolButtonBar = new ToolButtonBar(game,
+ gameActivity.getUser().getToolSlotSettings(),
+ (LinearLayout) layout.findViewById(R.id.game_toolbuttonbar));
+ shortMenu = new ShortMenu(game, gameActivity,
+ (LinearLayout) layout.findViewById(R.id.game_shortmenu));
+ gameOverMessage = new GameOverMessage(game, gameActivity,
+ layout.findViewById(R.id.game_game_over_message));
+ goalMessage = new GoalMessage(game, gameActivity, layout.findViewById(R.id.game_goal_message));
+ countdown = new Countdown(game, gameActivity.getTypeface(),
+ (TextView) layout.findViewById(R.id.game_countdown));
+ }
+ });
+ pauseButton = (ImageView) layout.findViewById(R.id.game_pausebutton);
+ pauseButton.setOnClickListener(this);
+ playerProgress = (TextView) layout.findViewById(R.id.game_playerprogress);
+ playerSpeed = (TextView) layout.findViewById(R.id.game_playerspeed);
+ fpsView = (TextView) layout.findViewById(R.id.game_fps);
+
+ fpsFormat = game.getContext().getString(R.string.game_fps_format_d);
+ playerProgressFormat = game.getContext().getString(R.string.game_playerprogress_format_f);
+ playerSpeedFormat = game.getContext().getString(R.string.game_playerspeed_format_f);
+ }
+
+ @Override
+ public void onClick(View v) {
+ game.tryToPause();
+ }
+
+ public void resetViews(final User user) {
+ gameViewHandler.startInUiThread(new Runnable() {
+ @Override
+ public void run() {
+ toolButtonBar.reset(user.getToolSlotSettings());
+ toolButtonBar.update(0);
+ playerProgress.setText(R.string.game_playerprogress_placeholder);
+ playerSpeed.setText(R.string.game_playerspeed_placeholder);
+ }
+ });
+ }
+
+ public void onGoalMessageKeyBack(){
+ goalMessage.onKeyBack();
+ }
+
+ public void showGameOverMessage() {
+ gameViewHandler.startInUiThread(new Runnable() {
+ @Override
+ public void run() {
+ gameOverMessage.fadeIn();
+ }
+ });
+ }
+
+ public void showGoalMessage(final LevelPack levelPack, final Level level){
+ gameViewHandler.startInUiThread(new Runnable() {
+ @Override
+ public void run() {
+ goalMessage.fadeInWithDelay(levelPack, level);
+ }
+ });
+ }
+
+ public void showShortMenu() {
+ shortMenu.startAnims();
+ shortMenu.setVisible(true);
+ }
+
+ public void hideShortMenu() {
+ shortMenu.setVisible(false);
+ }
+
+ public void startCountdown() {
+ countdown.start();
+ }
+
+ public void stopCountdown() {
+ countdown.stop();
+ }
+
+ public void update(final boolean gameRunning, final Timer timer, final float playerX, final float playerXMov) {
+ gameViewHandler.startInUiThread(new Runnable() {
+ @Override
+ public void run() {
+ fpsView.setText(String.format(fpsFormat, timer.getFps()));
+ if (gameRunning) {
+ playerProgress.setText(String.format(playerProgressFormat, toMeters(playerX)));
+ playerSpeed
+ .setText(String.format(playerSpeedFormat, toMeters(playerXMov * 1000)));
+ toolButtonBar.update(timer.getFrameTimeSeconds());
+ }
+ }
+ });
+ }
+
+ private float toMeters(float value) {
+ return ((int) (value * 20)) / 10f;
+ }
+
+}
diff --git a/app/src/main/java/de/frajul/endlessroll/views/WorldButton.java b/app/src/main/java/de/frajul/endlessroll/views/WorldButton.java
new file mode 100644
index 0000000..78abc77
--- /dev/null
+++ b/app/src/main/java/de/frajul/endlessroll/views/WorldButton.java
@@ -0,0 +1,86 @@
+package de.frajul.endlessroll.views;
+
+import android.graphics.Typeface;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import de.frajul.endlessroll.R;
+import de.frajul.endlessroll.levels.LevelPack;
+import de.frajul.endlessroll.main.GameActivity;
+
+/**
+ * Created by Julian on 01.08.2016.
+ */
+public class WorldButton implements View.OnClickListener {
+
+ private GameActivity gameActivity;
+ private LevelPack levelPack;
+ private WorldButtonOnClickListener clickListener;
+
+ private View layout;
+ private TextView title;
+ private ImageView previewImage;
+ private View lock;
+ private TextView levelCount;
+ private TextView starCount;
+ private TextView energyCount;
+
+ public WorldButton(GameActivity gameActivity, LevelPack levelPack, WorldButtonOnClickListener clickListener) {
+ this.gameActivity = gameActivity;
+ this.levelPack = levelPack;
+ this.clickListener = clickListener;
+
+ LayoutInflater inflater = LayoutInflater.from(gameActivity);
+ layout = inflater.inflate(R.layout.world_button, null);
+ layout.setOnClickListener(this);
+
+ Typeface typeface = gameActivity.getTypeface();
+ title = (TextView) layout.findViewById(R.id.worldbutton_title);
+ title.setTypeface(typeface);
+ previewImage = (ImageView) layout.findViewById(R.id.worldbutton_preview);
+ lock = layout.findViewById(R.id.worldbutton_lock);
+ levelCount = (TextView) layout.findViewById(R.id.worldbutton_levelcount);
+ levelCount.setTypeface(typeface);
+ starCount = (TextView) layout.findViewById(R.id.worldbutton_starcount);
+ starCount.setTypeface(typeface);
+ energyCount = (TextView) layout.findViewById(R.id.worldbutton_energycount);
+ energyCount.setTypeface(typeface);
+
+ update();
+ }
+
+ public void update() {
+ title.setText(levelPack.getName());
+ previewImage.setImageDrawable(
+ gameActivity.getResources().getDrawable(levelPack.getWorld().getPreviewId()));
+ levelCount.setText(gameActivity
+ .getString(R.string.world_button_count_format_dd, levelPack.getFinishedLevelCount(),
+ levelPack.getLevels().size()));
+ starCount.setText(gameActivity
+ .getString(R.string.world_button_count_format_dd, levelPack.getCollectedStarCount(),
+ levelPack.getAvailableStars()));
+ energyCount.setText(gameActivity.getString(R.string.world_button_count_format_dd,
+ levelPack.getCollectedEnergyCount(), levelPack.getAvailableEnergy()));
+ setLockVisible(levelPack.isLocked());
+ }
+
+ public void setLockVisible(boolean locked) {
+ int visibility = locked ? View.VISIBLE : View.GONE;
+ lock.setVisibility(visibility);
+ }
+
+ public LevelPack getLevelPack() {
+ return levelPack;
+ }
+
+ public View getView() {
+ return layout;
+ }
+
+ @Override
+ public void onClick(View v) {
+ clickListener.onClick(this);
+ }
+}
diff --git a/app/src/main/res/drawable/backgrounds_game_cave.png b/app/src/main/res/drawable/backgrounds_game_cave.png
new file mode 100644
index 0000000..8b373c1
Binary files /dev/null and b/app/src/main/res/drawable/backgrounds_game_cave.png differ
diff --git a/app/src/main/res/drawable/backgrounds_game_mountains.png b/app/src/main/res/drawable/backgrounds_game_mountains.png
new file mode 100644
index 0000000..6924868
Binary files /dev/null and b/app/src/main/res/drawable/backgrounds_game_mountains.png differ
diff --git a/app/src/main/res/drawable/backgrounds_menu_grass.png b/app/src/main/res/drawable/backgrounds_menu_grass.png
new file mode 100644
index 0000000..5a33827
Binary files /dev/null and b/app/src/main/res/drawable/backgrounds_menu_grass.png differ
diff --git a/app/src/main/res/drawable/black.png b/app/src/main/res/drawable/black.png
new file mode 100644
index 0000000..a90b596
Binary files /dev/null and b/app/src/main/res/drawable/black.png differ
diff --git a/app/src/main/res/drawable/guis_arrow_green.png b/app/src/main/res/drawable/guis_arrow_green.png
new file mode 100644
index 0000000..e5f24b6
Binary files /dev/null and b/app/src/main/res/drawable/guis_arrow_green.png differ
diff --git a/app/src/main/res/drawable/guis_clock.png b/app/src/main/res/drawable/guis_clock.png
new file mode 100644
index 0000000..8fd21b1
Binary files /dev/null and b/app/src/main/res/drawable/guis_clock.png differ
diff --git a/app/src/main/res/drawable/guis_goal.png b/app/src/main/res/drawable/guis_goal.png
new file mode 100644
index 0000000..f1ad4b1
Binary files /dev/null and b/app/src/main/res/drawable/guis_goal.png differ
diff --git a/app/src/main/res/drawable/guis_magnet_field.png b/app/src/main/res/drawable/guis_magnet_field.png
new file mode 100644
index 0000000..ca25b95
Binary files /dev/null and b/app/src/main/res/drawable/guis_magnet_field.png differ
diff --git a/app/src/main/res/drawable/guis_pausebutton.png b/app/src/main/res/drawable/guis_pausebutton.png
new file mode 100644
index 0000000..ba46ab0
Binary files /dev/null and b/app/src/main/res/drawable/guis_pausebutton.png differ
diff --git a/app/src/main/res/drawable/guis_playerarrow.png b/app/src/main/res/drawable/guis_playerarrow.png
new file mode 100644
index 0000000..7898595
Binary files /dev/null and b/app/src/main/res/drawable/guis_playerarrow.png differ
diff --git a/app/src/main/res/drawable/guis_radius.png b/app/src/main/res/drawable/guis_radius.png
new file mode 100644
index 0000000..b0c88ac
Binary files /dev/null and b/app/src/main/res/drawable/guis_radius.png differ
diff --git a/app/src/main/res/drawable/guis_settings_disabled.png b/app/src/main/res/drawable/guis_settings_disabled.png
new file mode 100644
index 0000000..aec0dfa
Binary files /dev/null and b/app/src/main/res/drawable/guis_settings_disabled.png differ
diff --git a/app/src/main/res/drawable/guis_settings_enabled.png b/app/src/main/res/drawable/guis_settings_enabled.png
new file mode 100644
index 0000000..e234ed8
Binary files /dev/null and b/app/src/main/res/drawable/guis_settings_enabled.png differ
diff --git a/app/src/main/res/drawable/guis_shape_shop_disabled.png b/app/src/main/res/drawable/guis_shape_shop_disabled.png
new file mode 100644
index 0000000..b31d48c
Binary files /dev/null and b/app/src/main/res/drawable/guis_shape_shop_disabled.png differ
diff --git a/app/src/main/res/drawable/guis_shape_shop_enabled.png b/app/src/main/res/drawable/guis_shape_shop_enabled.png
new file mode 100644
index 0000000..79847bc
Binary files /dev/null and b/app/src/main/res/drawable/guis_shape_shop_enabled.png differ
diff --git a/app/src/main/res/drawable/guis_sound_off.png b/app/src/main/res/drawable/guis_sound_off.png
new file mode 100644
index 0000000..151e850
Binary files /dev/null and b/app/src/main/res/drawable/guis_sound_off.png differ
diff --git a/app/src/main/res/drawable/guis_sound_on.png b/app/src/main/res/drawable/guis_sound_on.png
new file mode 100644
index 0000000..c939309
Binary files /dev/null and b/app/src/main/res/drawable/guis_sound_on.png differ
diff --git a/app/src/main/res/drawable/guis_splitter.png b/app/src/main/res/drawable/guis_splitter.png
new file mode 100644
index 0000000..c171f79
Binary files /dev/null and b/app/src/main/res/drawable/guis_splitter.png differ
diff --git a/app/src/main/res/drawable/guis_tick.png b/app/src/main/res/drawable/guis_tick.png
new file mode 100644
index 0000000..14092c2
Binary files /dev/null and b/app/src/main/res/drawable/guis_tick.png differ
diff --git a/app/src/main/res/drawable/playershapes_ball.png b/app/src/main/res/drawable/playershapes_ball.png
new file mode 100644
index 0000000..2a42ac4
Binary files /dev/null and b/app/src/main/res/drawable/playershapes_ball.png differ
diff --git a/app/src/main/res/drawable/playershapes_clock.png b/app/src/main/res/drawable/playershapes_clock.png
new file mode 100644
index 0000000..85ae88a
Binary files /dev/null and b/app/src/main/res/drawable/playershapes_clock.png differ
diff --git a/app/src/main/res/drawable/playershapes_pacman.png b/app/src/main/res/drawable/playershapes_pacman.png
new file mode 100644
index 0000000..d0f89a3
Binary files /dev/null and b/app/src/main/res/drawable/playershapes_pacman.png differ
diff --git a/app/src/main/res/drawable/terrain_t_grass.png b/app/src/main/res/drawable/terrain_t_grass.png
new file mode 100644
index 0000000..069be8f
Binary files /dev/null and b/app/src/main/res/drawable/terrain_t_grass.png differ
diff --git a/app/src/main/res/drawable/tools_bomb.png b/app/src/main/res/drawable/tools_bomb.png
new file mode 100644
index 0000000..da77e11
Binary files /dev/null and b/app/src/main/res/drawable/tools_bomb.png differ
diff --git a/app/src/main/res/drawable/tools_bomb_button.png b/app/src/main/res/drawable/tools_bomb_button.png
new file mode 100644
index 0000000..0a41ebf
Binary files /dev/null and b/app/src/main/res/drawable/tools_bomb_button.png differ
diff --git a/app/src/main/res/drawable/tools_button_empty.png b/app/src/main/res/drawable/tools_button_empty.png
new file mode 100644
index 0000000..4ace291
Binary files /dev/null and b/app/src/main/res/drawable/tools_button_empty.png differ
diff --git a/app/src/main/res/drawable/tools_button_locked.png b/app/src/main/res/drawable/tools_button_locked.png
new file mode 100644
index 0000000..5ae2f2a
Binary files /dev/null and b/app/src/main/res/drawable/tools_button_locked.png differ
diff --git a/app/src/main/res/drawable/tools_button_unlocked.png b/app/src/main/res/drawable/tools_button_unlocked.png
new file mode 100644
index 0000000..c657a9a
Binary files /dev/null and b/app/src/main/res/drawable/tools_button_unlocked.png differ
diff --git a/app/src/main/res/drawable/tools_magnet.png b/app/src/main/res/drawable/tools_magnet.png
new file mode 100644
index 0000000..0d6c0e2
Binary files /dev/null and b/app/src/main/res/drawable/tools_magnet.png differ
diff --git a/app/src/main/res/drawable/tools_magnet_button.png b/app/src/main/res/drawable/tools_magnet_button.png
new file mode 100644
index 0000000..71eae0a
Binary files /dev/null and b/app/src/main/res/drawable/tools_magnet_button.png differ
diff --git a/app/src/main/res/drawable/tools_ramp_button.png b/app/src/main/res/drawable/tools_ramp_button.png
new file mode 100644
index 0000000..fea06ee
Binary files /dev/null and b/app/src/main/res/drawable/tools_ramp_button.png differ
diff --git a/app/src/main/res/drawable/tools_spring.png b/app/src/main/res/drawable/tools_spring.png
new file mode 100644
index 0000000..23271f6
Binary files /dev/null and b/app/src/main/res/drawable/tools_spring.png differ
diff --git a/app/src/main/res/drawable/tools_spring_button.png b/app/src/main/res/drawable/tools_spring_button.png
new file mode 100644
index 0000000..3029602
Binary files /dev/null and b/app/src/main/res/drawable/tools_spring_button.png differ
diff --git a/app/src/main/res/drawable/world_previews_grass.png b/app/src/main/res/drawable/world_previews_grass.png
new file mode 100644
index 0000000..498940e
Binary files /dev/null and b/app/src/main/res/drawable/world_previews_grass.png differ
diff --git a/app/src/main/res/drawable/xml_background_bountymessage.xml b/app/src/main/res/drawable/xml_background_bountymessage.xml
new file mode 100644
index 0000000..8710c08
--- /dev/null
+++ b/app/src/main/res/drawable/xml_background_bountymessage.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/xml_background_levelbutton.xml b/app/src/main/res/drawable/xml_background_levelbutton.xml
new file mode 100644
index 0000000..55ab491
--- /dev/null
+++ b/app/src/main/res/drawable/xml_background_levelbutton.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/xml_background_toolslot.xml b/app/src/main/res/drawable/xml_background_toolslot.xml
new file mode 100644
index 0000000..29dbf0b
--- /dev/null
+++ b/app/src/main/res/drawable/xml_background_toolslot.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/xml_background_worldbutton.xml b/app/src/main/res/drawable/xml_background_worldbutton.xml
new file mode 100644
index 0000000..c2f3a75
--- /dev/null
+++ b/app/src/main/res/drawable/xml_background_worldbutton.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/xml_layers_toolprogressbar.xml b/app/src/main/res/drawable/xml_layers_toolprogressbar.xml
new file mode 100644
index 0000000..c0318d1
--- /dev/null
+++ b/app/src/main/res/drawable/xml_layers_toolprogressbar.xml
@@ -0,0 +1,21 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/xml_selector_gamebutton.xml b/app/src/main/res/drawable/xml_selector_gamebutton.xml
new file mode 100644
index 0000000..b4e0d6b
--- /dev/null
+++ b/app/src/main/res/drawable/xml_selector_gamebutton.xml
@@ -0,0 +1,30 @@
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/xml_selector_sound.xml b/app/src/main/res/drawable/xml_selector_sound.xml
new file mode 100644
index 0000000..61f796c
--- /dev/null
+++ b/app/src/main/res/drawable/xml_selector_sound.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/particlelab/src/androidTest/java/de/frajul/particlelab/ApplicationTest.java b/particlelab/src/androidTest/java/de/frajul/particlelab/ApplicationTest.java
new file mode 100644
index 0000000..788c0ba
--- /dev/null
+++ b/particlelab/src/androidTest/java/de/frajul/particlelab/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.example.particlelab;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/particlelab/src/main/java/de/frajul/particlelab/data/Color.java b/particlelab/src/main/java/de/frajul/particlelab/data/Color.java
new file mode 100644
index 0000000..098fcbd
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/data/Color.java
@@ -0,0 +1,69 @@
+package de.frajul.particlelab.data;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Color {
+
+ private float r, g, b;
+
+ public Color() {
+ }
+
+ public Color(float r, float g, float b){
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ }
+
+ public Color(Color other) {
+ this.r = other.getR();
+ this.g = other.getG();
+ this.b = other.getB();
+ }
+
+ public Color mix(float leftValue, float rightValue, Color color2) {
+ Color mySelf = new Color(this);
+ Color second = new Color(color2);
+ mySelf.mul(leftValue);
+ second.mul(rightValue);
+ mySelf.add(second);
+ return mySelf;
+ }
+
+ private void mul(float a) {
+ r *= a;
+ g *= a;
+ b *= a;
+ }
+
+ private void add(Color other) {
+ r += other.getR();
+ g += other.getG();
+ b += other.getB();
+ }
+
+ public float getR() {
+ return r;
+ }
+
+ public void setR(float r) {
+ this.r = r;
+ }
+
+ public float getG() {
+ return g;
+ }
+
+ public void setG(float g) {
+ this.g = g;
+ }
+
+ public float getB() {
+ return b;
+ }
+
+ public void setB(float b) {
+ this.b = b;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/data/Vector.java b/particlelab/src/main/java/de/frajul/particlelab/data/Vector.java
new file mode 100644
index 0000000..9580bd7
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/data/Vector.java
@@ -0,0 +1,101 @@
+package de.frajul.particlelab.data;
+
+/**
+ * Created by Julian on 01.12.2015.
+ */
+public class Vector {
+
+ public float x, y;
+
+ public Vector() {
+ this(0, 0);
+ }
+
+ public Vector(Vector other) {
+ this(other.x, other.y);
+ }
+
+ public Vector(float x, float y) {
+ set(x, y);
+ }
+
+ @Override
+ public String toString() {
+ return "Vector(" + x + ", " + y + ")";
+ }
+
+ public Vector set(float x, float y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ }
+
+ public float length() {
+ return (float) Math.sqrt(x * x + y * y);
+ }
+
+ public Vector normalize() {
+ float length = this.length();
+ x /= length;
+ y /= length;
+ return this;
+ }
+
+ public Vector translate(Vector other) {
+ this.x += other.x;
+ this.y += other.y;
+ return this;
+ }
+
+ public Vector translate(float x, float y) {
+ this.x += x;
+ this.y += y;
+ return this;
+ }
+
+ public Vector mul(float z) {
+ x *= z;
+ y *= z;
+ return this;
+ }
+
+ public Vector mul(Vector other) {
+ this.x *= other.x;
+ this.y *= other.y;
+ return this;
+ }
+
+ public Vector mul(float x, float y) {
+ this.x *= x;
+ this.y *= y;
+ return this;
+ }
+
+ public Vector negate() {
+ this.x *= -1;
+ this.y *= -1;
+ return this;
+ }
+
+ public Vector vectorTo(Vector other) {
+ float x = other.x - this.x;
+ float y = other.y - this.y;
+ return new Vector(x, y);
+ }
+
+ public void setX(float x) {
+ this.x = x;
+ }
+
+ public void setY(float y) {
+ this.y = y;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/Entity.java b/particlelab/src/main/java/de/frajul/particlelab/entities/Entity.java
new file mode 100644
index 0000000..790b8c7
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/Entity.java
@@ -0,0 +1,80 @@
+package de.frajul.particlelab.entities;
+
+import de.frajul.particlelab.data.Color;
+import de.frajul.particlelab.entities.textures.Texture;
+import de.frajul.particlelab.data.Vector;
+
+/**
+ * Created by Julian on 20.11.2015.
+ */
+public class Entity extends Quad {
+
+ private Color color;
+ private Texture texture;
+ private Vector movement;
+ private float rotation;
+ private float dynamicRotation;
+ private float alpha = 1.0f;
+ private boolean destroyed;
+
+ public Entity(Texture texture, Vector position, float width, float height) {
+ super(position, width, height);
+ this.texture = texture;
+ this.movement = new Vector();
+ }
+
+ public Entity(Texture texture, Color color, Vector position, float width, float height) {
+ super(position, width, height);
+ this.texture = texture;
+ this.color = color;
+ this.movement = new Vector();
+ }
+
+ public void move(Vector movement) {
+ position.translate(movement);
+ }
+
+ public void rotate(float factor) {
+ rotation += factor;
+ }
+
+ public Texture getTexture() {
+ return texture;
+ }
+
+ public void setTexture(Texture texture) {
+ this.texture = texture;
+ }
+
+ public Vector getMovement() {
+ return movement;
+ }
+
+ public void setMovement(Vector movement) {
+ this.movement = movement;
+ }
+
+ public float getRotation() {
+ return rotation;
+ }
+
+ public void setRotation(float rotation) {
+ this.rotation = rotation;
+ }
+
+ public void setAlpha(float alpha) {
+ this.alpha = alpha;
+ }
+
+ public float getAlpha() {
+ return alpha;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/Quad.java b/particlelab/src/main/java/de/frajul/particlelab/entities/Quad.java
new file mode 100644
index 0000000..aae8a86
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/Quad.java
@@ -0,0 +1,59 @@
+package de.frajul.particlelab.entities;
+
+import de.frajul.particlelab.data.Vector;
+
+/**
+ * Created by Julian on 01.12.2015.
+ */
+public class Quad {
+
+ protected Vector position;
+ protected float width, height;
+
+ public Quad(Vector position, float width, float height) {
+ this.position = position;
+ this.width = width;
+ this.height = height;
+ }
+
+ public float getRightEdge() {
+ return position.x + width / 2;
+ }
+
+ public float getLeftEdge() {
+ return position.x - width / 2;
+ }
+
+ public float getTopEdge() {
+ return position.y + height / 2;
+ }
+
+ public float getBottomEdge() {
+ return position.y - height / 2;
+ }
+
+ public Vector getPosition() {
+ return position;
+ }
+
+ public void setPosition(Vector position) {
+ this.position = position;
+ }
+
+ public float getWidth() {
+ return width;
+ }
+
+ public void setWidth(float width) {
+ this.width = width;
+ }
+
+ public float getHeight() {
+ return height;
+ }
+
+ public void setHeight(float height) {
+ this.height = height;
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/Particle.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/Particle.java
new file mode 100644
index 0000000..21ffd64
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/Particle.java
@@ -0,0 +1,89 @@
+package de.frajul.particlelab.entities.particles;
+
+import de.frajul.particlelab.data.Vector;
+import de.frajul.particlelab.entities.Entity;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Range;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.TintTimeline;
+import de.frajul.particlelab.main.GameLog;
+import de.frajul.particlelab.rendering.Timer;
+
+import java.util.Random;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Particle extends Entity {
+
+ private Random random;
+ private boolean active;
+ private float maxLife;
+ private float passedLifetime;
+
+ private Timeline scaleTimeline;
+ private Range scale;
+ private Timeline velocityTimeline;
+ private Range velocity;
+ private Timeline angleTimeline;
+ private Range angle;
+ private Timeline rotationTimeline;
+ private Range rotation;
+ private Timeline transparencyTimeline;
+ private TintTimeline tintTimeline;
+
+ public Particle(Random random) {
+ super(null, new Vector(), 1, 1);
+ this.random = random;
+ }
+
+ public void activate(Vector position, float liveValue, ParticleData particleData) {
+ active = true;
+ passedLifetime = 0;
+ super.setPosition(position);
+ maxLife = particleData.getLife().createValue(random, liveValue);
+ scaleTimeline = particleData.getScaleTR().getTimeline();
+ scale = particleData.getScaleTR().getRange().createNormalizedInstance(random);
+ velocityTimeline = particleData.getVelocityTR().getTimeline();
+ velocity = particleData.getVelocityTR().getRange().createNormalizedInstance(random);
+ angleTimeline = particleData.getAngleTR().getTimeline();
+ angle = particleData.getAngleTR().getRange().createNormalizedInstance(random);
+ rotationTimeline = particleData.getRotationTR().getTimeline();
+ rotation = particleData.getRotationTR().getRange().createNormalizedInstance(random);
+ transparencyTimeline = particleData.getTransparencyT();
+ tintTimeline = particleData.getTint();
+ }
+
+ public void update(Timer timer, float windStrength, float gravityStrength) {
+ if (active) {
+ passedLifetime += timer.getFrameTime();
+ if (passedLifetime >= maxLife)
+ active = false;
+ float lifetimePercent = passedLifetime / maxLife;
+ setScale(scale.createValue(random, scaleTimeline.getValueAtTime(lifetimePercent)));
+ setMovement(velocity.createValue(random, velocityTimeline.getValueAtTime(lifetimePercent)),
+ angle.createValue(random, angleTimeline.getValueAtTime(lifetimePercent)), windStrength, gravityStrength);
+ super.setRotation(-rotation.createValue(random, rotationTimeline.getValueAtTime(lifetimePercent)));
+ super.setAlpha(transparencyTimeline.getValueAtTime(lifetimePercent));
+ super.setColor(tintTimeline.getValueAtTime(lifetimePercent));
+ }
+ }
+
+ private void setScale(float scale) {
+ super.setWidth(scale * ParticleSystem.TRANSFER_VALUE);
+ super.setHeight(scale * ParticleSystem.TRANSFER_VALUE);
+ }
+
+ private void setMovement(float velocity, float angle, float windStrength, float gravityStrength) {
+ float radians = (float) Math.toRadians(angle);
+ Vector normalMovement = new Vector();
+ normalMovement.setX((float) Math.cos(radians));
+ normalMovement.setY((float) Math.sin(radians));
+ normalMovement.mul(velocity * ParticleSystem.TRANSFER_VALUE);
+ normalMovement.translate(windStrength * ParticleSystem.TRANSFER_VALUE, gravityStrength * ParticleSystem.TRANSFER_VALUE);
+ super.setMovement(normalMovement);
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleData.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleData.java
new file mode 100644
index 0000000..af7e29c
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleData.java
@@ -0,0 +1,59 @@
+package de.frajul.particlelab.entities.particles;
+
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Range;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.TimelineRange;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.TintTimeline;
+import de.frajul.particlelab.entities.textures.Texture;
+
+/**
+ * Created by Julian on 03.08.2016.
+ */
+public class ParticleData {
+
+ private Range life;
+ private TimelineRange scaleTR;
+ private TimelineRange velocityTR;
+ private TimelineRange angleTR;
+ private TimelineRange rotationTR;
+ private Timeline transparencyT;
+ private TintTimeline tint;
+
+ public ParticleData(Range life, TimelineRange scaleTR, TimelineRange velocityTR, TimelineRange angleTR, TimelineRange rotationTR, Timeline transparencyT, TintTimeline tint) {
+ this.life = life;
+ this.scaleTR = scaleTR;
+ this.velocityTR = velocityTR;
+ this.angleTR = angleTR;
+ this.rotationTR = rotationTR;
+ this.transparencyT = transparencyT;
+ this.tint = tint;
+ }
+
+ public Range getLife() {
+ return life;
+ }
+
+ public TimelineRange getScaleTR() {
+ return scaleTR;
+ }
+
+ public TimelineRange getVelocityTR() {
+ return velocityTR;
+ }
+
+ public TimelineRange getAngleTR() {
+ return angleTR;
+ }
+
+ public TimelineRange getRotationTR() {
+ return rotationTR;
+ }
+
+ public Timeline getTransparencyT() {
+ return transparencyT;
+ }
+
+ public TintTimeline getTint() {
+ return tint;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleEffect.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleEffect.java
new file mode 100644
index 0000000..34a40c4
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleEffect.java
@@ -0,0 +1,218 @@
+package de.frajul.particlelab.entities.particles;
+
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Options;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Range;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.SpawnShape;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.TimelineRange;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.TintTimeline;
+import de.frajul.particlelab.entities.textures.Texture;
+import de.frajul.particlelab.rendering.Timer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Created by Julian on 05.08.2016.
+ */
+public class ParticleEffect {
+
+ private TimelineRange life;
+ //Particle Timeline
+ private TimelineRange scale;
+ private TimelineRange velocity;
+ private TimelineRange angle;
+ private TimelineRange rotation;
+ private Timeline transparency;
+ private TintTimeline tint;
+
+ //Source Timeline
+ private Range delay;
+ private Range duration;
+ private TimelineRange emission;
+ private Range xOffset;
+ private Range yOffset;
+ private SpawnShape.Shape spawnShape;
+ private TimelineRange spawnWidth;
+ private TimelineRange spawnHeight;
+ private TimelineRange wind;
+ private TimelineRange gravity;
+
+ private Options options;
+ private Texture texture;
+ private String textureName;
+
+ private Random random;
+ private List sources = new ArrayList<>();
+
+ public ParticleEffect() {
+ this.random = new Random();
+ }
+
+ public void update(Timer timer) {
+ for (ParticleSource source : sources)
+ source.update(timer);
+ }
+
+ public ParticleData createParticleData(){
+ return new ParticleData(life.getRange(), scale, velocity, angle, rotation, transparency, tint);
+ }
+
+ public Random getRandom() {
+ return random;
+ }
+
+ public void addSource(ParticleSource source) {
+ sources.add(source);
+ }
+
+ public void setDelay(Range delay) {
+ this.delay = delay;
+ }
+
+ public void setDuration(Range duration) {
+ this.duration = duration;
+ }
+
+ public void setEmission(TimelineRange emission) {
+ this.emission = emission;
+ }
+
+ public void setLife(TimelineRange life) {
+ this.life = life;
+ }
+
+ public void setxOffset(Range xOffset) {
+ this.xOffset = xOffset;
+ }
+
+ public void setyOffset(Range yOffset) {
+ this.yOffset = yOffset;
+ }
+
+ public void setSpawnShape(SpawnShape.Shape spawnShape) {
+ this.spawnShape = spawnShape;
+ }
+
+ public void setSpawnWidth(TimelineRange spawnWidth) {
+ this.spawnWidth = spawnWidth;
+ }
+
+ public void setSpawnHeight(TimelineRange spawnHeight) {
+ this.spawnHeight = spawnHeight;
+ }
+
+ public void setScale(TimelineRange scale) {
+ this.scale = scale;
+ }
+
+ public void setVelocity(TimelineRange velocity) {
+ this.velocity = velocity;
+ }
+
+ public void setAngle(TimelineRange angle) {
+ this.angle = angle;
+ }
+
+ public void setRotation(TimelineRange rotation) {
+ this.rotation = rotation;
+ }
+
+ public void setWind(TimelineRange wind) {
+ this.wind = wind;
+ }
+
+ public void setGravity(TimelineRange gravity) {
+ this.gravity = gravity;
+ }
+
+ public void setTint(TintTimeline tint) {
+ this.tint = tint;
+ }
+
+ public void setTransparency(Timeline transparency) {
+ this.transparency = transparency;
+ }
+
+ public Timeline getTransparency() {
+ return transparency;
+ }
+
+ public TintTimeline getTint() {
+ return tint;
+ }
+
+ public void setOptions(Options options) {
+ this.options = options;
+ }
+
+ public void setTexture(Texture texture) {
+ this.texture = texture;
+ }
+
+ public void setTextureName(String textureName) {
+ this.textureName = textureName;
+ }
+
+ public String getTextureName() {
+ return textureName;
+ }
+
+ public Texture getTexture() {
+ return texture;
+ }
+
+ public Range getDelay() {
+ return delay;
+ }
+
+ public Range getDuration() {
+ return duration;
+ }
+
+ public TimelineRange getEmission() {
+ return emission;
+ }
+
+ public Options getOptions() {
+ return options;
+ }
+
+ public TimelineRange getWind() {
+ return wind;
+ }
+
+ public TimelineRange getGravity() {
+ return gravity;
+ }
+
+ public Range getxOffset() {
+ return xOffset;
+ }
+
+ public Range getyOffset() {
+ return yOffset;
+ }
+
+ public List getSources() {
+ return sources;
+ }
+
+ public TimelineRange getLife() {
+ return life;
+ }
+
+ public SpawnShape.Shape getSpawnShape() {
+ return spawnShape;
+ }
+
+ public TimelineRange getSpawnHeight() {
+ return spawnHeight;
+ }
+
+ public TimelineRange getSpawnWidth() {
+ return spawnWidth;
+ }
+}
+
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleReader.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleReader.java
new file mode 100644
index 0000000..5a3c3fb
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleReader.java
@@ -0,0 +1,164 @@
+package de.frajul.particlelab.entities.particles;
+
+import android.content.Context;
+
+import de.frajul.particlelab.entities.particles.attributes.Attribute;
+import de.frajul.particlelab.entities.particles.attributes.AttributeValueReader;
+import de.frajul.particlelab.entities.particles.attributes.ParticleAttributeType;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.ImagePath;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Options;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.ParticleAttributeValueType;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Range;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.SpawnShape;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.TimelineRange;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.TintTimeline;
+import de.frajul.particlelab.main.GameLog;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class ParticleReader {
+
+ private Context context;
+ private AttributeValueReader attributeValueReader;
+
+ private List attributes;
+ private Attribute currentAttribute;
+
+ public ParticleReader(Context context) {
+ this.context = context;
+ attributeValueReader = new AttributeValueReader();
+ }
+
+ /**
+ * reads ParticleEffect from *.pe files
+ * !Ignores COUNT, LIFE_OFFSET!
+ */
+ public ParticleEffect read(String filename) throws Exception {
+ try {
+ attributes = new ArrayList<>();
+ currentAttribute = null;
+ InputStream is = context.getAssets().open(filename);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ handleLine(line);
+ }
+ reader.close();
+ return createParticleEffect();
+ } catch (Exception e) {
+ throw new Exception("Could not read particleFile: ", e);
+ }
+ }
+
+ private void handleLine(String line) throws Exception {
+ if (line.startsWith("- ")) {
+ Attribute attrib = ParticleAttributeType.getByInFileTitle(line).createInstance();
+ attributes.add(attrib);
+ currentAttribute = attrib;
+ } else if (currentAttribute != null)
+ attributeValueReader.addValueForAttribute(currentAttribute, line);
+ }
+
+ private ParticleEffect createParticleEffect() throws Exception {
+ ParticleEffect effect = new ParticleEffect();
+ Timeline timeline = null;
+ Range range = null;
+ for (Attribute attribute : attributes) {
+ switch (attribute.getType()) {
+ case DELAY:
+ effect.setDelay((Range) attribute.get(ParticleAttributeValueType.RANGE));
+ break;
+ case DURATION:
+ effect.setDuration((Range) attribute.get(ParticleAttributeValueType.RANGE));
+ break;
+ case COUNT:
+ break;
+ case EMISSION:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setEmission(new TimelineRange(timeline, range));
+ break;
+ case LIFE:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setLife(new TimelineRange(timeline, range));
+ break;
+ case LIFE_OFFSET:
+ break;
+ case X_OFFSET:
+ effect.setxOffset((Range) attribute.get(ParticleAttributeValueType.RANGE));
+ break;
+ case Y_OFFSET:
+ effect.setyOffset((Range) attribute.get(ParticleAttributeValueType.RANGE));
+ break;
+ case SPAWN_SHAPE:
+ effect.setSpawnShape(((SpawnShape) attribute.get(ParticleAttributeValueType.SPAWN_SHAPE)).getShape());
+ break;
+ case SPAWN_WIDTH:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setSpawnWidth(new TimelineRange(timeline, range));
+ break;
+ case SPAWN_HEIGHT:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setSpawnHeight(new TimelineRange(timeline, range));
+ break;
+ case SCALE:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setScale(new TimelineRange(timeline, range));
+ break;
+ case VELOCITY:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setVelocity(new TimelineRange(timeline, range));
+ break;
+ case ANGLE:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setAngle(new TimelineRange(timeline, range));
+ break;
+ case ROTATION:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setRotation(new TimelineRange(timeline, range));
+ break;
+ case WIND:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setWind(new TimelineRange(timeline, range));
+ break;
+ case GRAVITY:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ range = (Range) attribute.get(ParticleAttributeValueType.RANGE);
+ effect.setGravity(new TimelineRange(timeline, range));
+ break;
+ case TINT:
+ effect.setTint((TintTimeline) attribute.get(ParticleAttributeValueType.TINT_TIMELINE));
+ break;
+ case TRANSPARENCY:
+ timeline = (Timeline) attribute.get(ParticleAttributeValueType.TIMELINE);
+ effect.setTransparency(timeline);
+ break;
+ case OPTIONS:
+ effect.setOptions((Options) attribute.get(ParticleAttributeValueType.OPTIONS));
+ break;
+ case IMAGE_PATH:
+ String path = ((ImagePath) attribute.get(ParticleAttributeValueType.IMAGE_PATH)).getImagePath();
+ effect.setTextureName(path);
+ break;
+ }
+ }
+ return effect;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleSource.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleSource.java
new file mode 100644
index 0000000..951dff4
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleSource.java
@@ -0,0 +1,150 @@
+package de.frajul.particlelab.entities.particles;
+
+import de.frajul.particlelab.data.Vector;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.SpawnShape;
+import de.frajul.particlelab.main.GameLog;
+import de.frajul.particlelab.rendering.Lock;
+import de.frajul.particlelab.rendering.Timer;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class ParticleSource {
+
+ private Vector position;
+ private ParticleEffect effect;
+ private Random random;
+
+ private boolean alife;
+ private float currentDelay;
+ private float lifePercent;
+ private float maxTime = -1;
+ private float passedTime;
+ private float emittPause;
+ private float passedEmittPause;
+ private float passedSecond;
+ private float windStrength;
+ private float gravityStrength;
+ private Vector spawnSize = null;
+
+ private ParticleData particleData;
+ private List activeParticles = new ArrayList<>();
+ private List inactiveParticles = new ArrayList<>();
+ private Lock activeParticleLock = new Lock();
+
+ public ParticleSource(Vector position, ParticleEffect effect) {
+ this.position = position;
+ this.effect = effect;
+ random = effect.getRandom();
+ effect.addSource(this);
+ }
+
+ public void start() {
+ alife = true;
+ currentDelay = effect.getDelay().createValue(random, 0);
+ maxTime = effect.getDuration().createValue(random, 0) + currentDelay;
+ passedTime = 0;
+ emittPause = calcEmittPause();
+ passedEmittPause = 0;
+ passedSecond = 0;
+ lifePercent = 0;
+ }
+
+ public void update(Timer timer) {
+ if (alife) {
+ passedTime += timer.getFrameTime();
+ lifePercent = passedTime / maxTime;
+
+ if (passedTime >= currentDelay) {
+ passedEmittPause += timer.getFrameTime();
+ calcWindAndGravity();
+ while (passedEmittPause >= emittPause) {
+ passedEmittPause -= emittPause;
+ emitt();
+ }
+
+ passedSecond += timer.getFrameTime();
+ if (passedSecond >= 1000) {
+ passedSecond -= 1000;
+ calcEmittPause();
+ }
+ }
+ if (passedTime >= maxTime)
+ die();
+ }
+ updateParticles(timer);
+ }
+
+ private void updateParticles(Timer timer) {
+ activeParticleLock.lock();
+ Iterator iter = activeParticles.iterator();
+ while (iter.hasNext()) {
+ Particle particle = iter.next();
+ particle.update(timer, windStrength, gravityStrength);
+ if (!particle.isActive()) {
+ inactiveParticles.add(particle);
+ iter.remove();
+ } else {
+ particle.move(new Vector(particle.getMovement()).mul(timer.getFrameTime() / 1000));
+ }
+ }
+ activeParticleLock.unlock();
+ }
+
+ private void die() {
+ alife = false;
+ if (effect.getOptions().isContinuous())
+ start();
+ }
+
+ public void emitt() {
+ if (particleData == null)
+ particleData = effect.createParticleData();
+ float xOff = effect.getxOffset().createValue(random, 0) * ParticleSystem.TRANSFER_VALUE;
+ float yOff = effect.getyOffset().createValue(random, 0) * ParticleSystem.TRANSFER_VALUE;
+ if (effect.getSpawnShape() == SpawnShape.Shape.SQUARE) {
+ float width = effect.getSpawnWidth().getRange().createValue(random, effect.getSpawnWidth().getTimeline().getValueAtTime(lifePercent));
+ float height = effect.getSpawnHeight().getRange().createValue(random, effect.getSpawnHeight().getTimeline().getValueAtTime(lifePercent));
+ xOff += (random.nextFloat() * width - width * 0.5f)*ParticleSystem.TRANSFER_VALUE;
+ yOff += (random.nextFloat() * height - height * 0.5f)*ParticleSystem.TRANSFER_VALUE;
+ }
+ setUpParticle(new Vector(position).translate(xOff, yOff));
+ }
+
+ private Particle setUpParticle(Vector position) {
+ Particle particle;
+ if (inactiveParticles.size() > 0)
+ particle = inactiveParticles.remove(0);
+ else
+ particle = new Particle(random);
+
+ particle.activate(position, effect.getLife().getTimeline().getValueAtTime(lifePercent), particleData);
+ activeParticleLock.lock();
+ activeParticles.add(particle);
+ activeParticleLock.unlock();
+ return particle;
+ }
+
+ private void calcWindAndGravity() {
+ windStrength = effect.getWind().getRange().createValue(random, effect.getWind().getTimeline().getValueAtTime(lifePercent));
+ gravityStrength = effect.getGravity().getRange().createValue(random, effect.getGravity().getTimeline().getValueAtTime(lifePercent));
+ }
+
+ private float calcEmittPause() {
+ float emittedPerSecond = effect.getEmission().getRange().createValue(random, effect.getEmission().getTimeline().getValueAtTime(passedTime / maxTime));
+ return 1000 / emittedPerSecond;
+ }
+
+ public Lock getActiveParticleLock() {
+ return activeParticleLock;
+ }
+
+ public List getActiveParticles() {
+ return activeParticles;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleSystem.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleSystem.java
new file mode 100644
index 0000000..652144b
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/ParticleSystem.java
@@ -0,0 +1,42 @@
+package de.frajul.particlelab.entities.particles;
+
+import android.content.Context;
+
+import de.frajul.particlelab.entities.textures.TextureLoader;
+import de.frajul.particlelab.rendering.Timer;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class ParticleSystem {
+
+ public static final float TRANSFER_VALUE = 0.002f;
+ public final ParticleEffect explosion;
+ public final ParticleEffect firework;
+ private ParticleEffect[] effects;
+
+ private TextureLoader textureLoader;
+
+ public ParticleSystem(Context context) throws Exception {
+ this.textureLoader = new TextureLoader(context);
+ ParticleReader reader = new ParticleReader(context);
+ explosion = reader.read("explosion.pe");
+ firework = reader.read("firework.pe");
+
+ effects = new ParticleEffect[]{explosion, firework};
+ }
+
+ public void update(Timer timer) {
+ for (ParticleEffect effect : effects)
+ effect.update(timer);
+ }
+
+ public void loadTextures() throws Exception {
+ for (ParticleEffect effect : effects)
+ effect.setTexture(textureLoader.loadTexture(effect.getTextureName()));
+ }
+
+ public ParticleEffect[] getEffects() {
+ return effects;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/Attribute.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/Attribute.java
new file mode 100644
index 0000000..8caaa7e
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/Attribute.java
@@ -0,0 +1,33 @@
+package de.frajul.particlelab.entities.particles.attributes;
+
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.ParticleAttributeValue;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.ParticleAttributeValueType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Attribute {
+
+ private ParticleAttributeType type;
+ private List values = new ArrayList<>();
+
+ public Attribute(ParticleAttributeType type, ParticleAttributeValueType... valueTypes) {
+ this.type = type;
+ for (ParticleAttributeValueType valueType : valueTypes)
+ values.add(valueType.createInstance());
+ }
+
+ public ParticleAttributeValue get(ParticleAttributeValueType valueType) throws Exception {
+ for (ParticleAttributeValue v : values)
+ if (v.getType() == valueType)
+ return v;
+ throw new Exception("ParticleAttributeValue with type: " + valueType + " does not exist in Attribute "+type);
+ }
+
+ public ParticleAttributeType getType() {
+ return type;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/AttributeValueReader.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/AttributeValueReader.java
new file mode 100644
index 0000000..fc0f83c
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/AttributeValueReader.java
@@ -0,0 +1,77 @@
+package de.frajul.particlelab.entities.particles.attributes;
+
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.ImagePath;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Options;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.ParticleAttributeValueType;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Range;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.SpawnShape;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Timeline;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.TintTimeline;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class AttributeValueReader {
+
+ public void addValueForAttribute(Attribute attribute, String line) throws Exception {
+ //RANGE
+ if (line.startsWith("lowMin:") || line.startsWith("min:"))
+ ((Range) attribute.get(ParticleAttributeValueType.RANGE)).setLowMin(parseFloat(line));
+ else if (line.startsWith("lowMax:") || line.startsWith("max:"))
+ ((Range) attribute.get(ParticleAttributeValueType.RANGE)).setLowMax(parseFloat(line));
+ else if (line.startsWith("highMin:"))
+ ((Range) attribute.get(ParticleAttributeValueType.RANGE)).setHighMin(parseFloat(line));
+ else if (line.startsWith("highMax:"))
+ ((Range) attribute.get(ParticleAttributeValueType.RANGE)).setHighMax(parseFloat(line));
+ //TIMELINE
+ else if (!line.startsWith("scalingCount") && line.startsWith("scaling"))
+ ((Timeline) attribute.get(ParticleAttributeValueType.TIMELINE)).setValueOfPoint(parseTimeLineIndex("scaling", line), parseFloat(line));
+ else if (!line.startsWith("timelineCount") && line.startsWith("timeline") && attribute.getType() != ParticleAttributeType.TINT)
+ ((Timeline) attribute.get(ParticleAttributeValueType.TIMELINE)).setTimeOfPoint(parseTimeLineIndex("timeline", line), parseFloat(line));
+ //TINT_TIMELINE
+ else if (!line.startsWith("colorsCount") && line.startsWith("colors")) {
+ int index = parseTimeLineIndex("colors", line);
+ ((TintTimeline) attribute.get(ParticleAttributeValueType.TINT_TIMELINE)).setValueOfPoint((int) (index / 3f), index % 3, parseFloat(line));
+ } else if (!line.startsWith("timelineCount") && line.startsWith("timeline") && attribute.getType() == ParticleAttributeType.TINT)
+ ((TintTimeline) attribute.get(ParticleAttributeValueType.TINT_TIMELINE)).setTimeOfPoint(parseTimeLineIndex("timeline", line), parseFloat(line));
+ //SPAWN_SHAPE
+ else if (line.startsWith("shape:"))
+ ((SpawnShape) attribute.get(ParticleAttributeValueType.SPAWN_SHAPE)).setShape(SpawnShape.Shape.byName(parseString(line)));
+ //OPTIONS
+ else if (line.startsWith("attached:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setAttached(parseBoolean(line));
+ else if (line.startsWith("continuous:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setContinuous(parseBoolean(line));
+ else if (line.startsWith("aligned:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setAligned(parseBoolean(line));
+ else if (line.startsWith("additive:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setAdditive(parseBoolean(line));
+ else if (line.startsWith("behind:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setBehind(parseBoolean(line));
+ else if (line.startsWith("premultipliedAlpha:"))
+ ((Options) attribute.get(ParticleAttributeValueType.OPTIONS)).setPremultipliedAlpha(parseBoolean(line));
+ //IMAGE PATH
+ else if (attribute.getType() == ParticleAttributeType.IMAGE_PATH)
+ ((ImagePath) attribute.get(ParticleAttributeValueType.IMAGE_PATH)).setImagePath(line);
+ }
+
+ private int parseTimeLineIndex(String start, String line) throws Exception {
+ String asString = line.split(start)[1].split(":")[0];
+ return Integer.parseInt(asString);
+ }
+
+ private float parseFloat(String line) throws Exception {
+ String asString = line.split(" ")[1];
+ return Float.parseFloat(asString);
+ }
+
+ private String parseString(String line) throws Exception {
+ return line.split(" ")[1];
+ }
+
+ private boolean parseBoolean(String line) throws Exception {
+ String asString = line.split(" ")[1];
+ return Boolean.parseBoolean(asString);
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/ParticleAttributeType.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/ParticleAttributeType.java
new file mode 100644
index 0000000..d63939d
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/ParticleAttributeType.java
@@ -0,0 +1,55 @@
+package de.frajul.particlelab.entities.particles.attributes;
+
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.ParticleAttributeValueType;
+
+public enum ParticleAttributeType {
+ NONE(""),
+ DELAY("Delay", ParticleAttributeValueType.RANGE),
+ DURATION("Duration", ParticleAttributeValueType.RANGE),
+ COUNT("Count", ParticleAttributeValueType.RANGE),
+ EMISSION("Emission", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ LIFE("Life", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ LIFE_OFFSET("Life Offset", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ X_OFFSET("X Offset", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ Y_OFFSET("Y Offset", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ SPAWN_SHAPE("Spawn Shape", ParticleAttributeValueType.SPAWN_SHAPE),
+ SPAWN_WIDTH("Spawn Width", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ SPAWN_HEIGHT("Spawn Height", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ SCALE("Scale", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ VELOCITY("Velocity", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ ANGLE("Angle", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ ROTATION("Rotation", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ WIND("Wind", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ GRAVITY("Gravity", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ TINT("Tint", ParticleAttributeValueType.TINT_TIMELINE),
+ TRANSPARENCY("Transparency", ParticleAttributeValueType.RANGE, ParticleAttributeValueType.TIMELINE),
+ OPTIONS("Options", ParticleAttributeValueType.OPTIONS),
+ IMAGE_PATH("Image Path", ParticleAttributeValueType.IMAGE_PATH);
+
+ private String name;
+ private ParticleAttributeValueType[] valueTypes;
+
+ ParticleAttributeType(String name, ParticleAttributeValueType... valueTypes) {
+ this.name = name;
+ this.valueTypes = valueTypes;
+ }
+
+ private String getInFileTitle() {
+ return "- " + name + " -";
+ }
+
+ public static ParticleAttributeType getByInFileTitle(String title) throws Exception {
+ for (ParticleAttributeType setting : values()) {
+ if (setting != NONE && setting.getInFileTitle().equals(title))
+ return setting;
+ }
+ throw new Exception("Could not find ParticleAttributeType by title: " + title);
+ }
+
+ public Attribute createInstance() throws Exception {
+ if (this == NONE)
+ throw new Exception("Cannot create Instance from Attribute NONE");
+ return new Attribute(this, valueTypes);
+ }
+
+}
\ No newline at end of file
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/ImagePath.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/ImagePath.java
new file mode 100644
index 0000000..37cd812
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/ImagePath.java
@@ -0,0 +1,21 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class ImagePath extends ParticleAttributeValue {
+
+ private String imagePath;
+
+ public ImagePath() {
+ super(ParticleAttributeValueType.IMAGE_PATH);
+ }
+
+ public String getImagePath() {
+ return imagePath;
+ }
+
+ public void setImagePath(String imagePath) {
+ this.imagePath = imagePath;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/Options.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/Options.java
new file mode 100644
index 0000000..04e2348
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/Options.java
@@ -0,0 +1,66 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Options extends ParticleAttributeValue {
+
+ private boolean attached;
+ private boolean continuous;
+ private boolean aligned;
+ private boolean additive;
+ private boolean behind;
+ private boolean premultipliedAlpha;
+
+ public Options() {
+ super(ParticleAttributeValueType.OPTIONS);
+ }
+
+ public boolean isAttached() {
+ return attached;
+ }
+
+ public void setAttached(boolean attached) {
+ this.attached = attached;
+ }
+
+ public boolean isContinuous() {
+ return continuous;
+ }
+
+ public void setContinuous(boolean continuous) {
+ this.continuous = continuous;
+ }
+
+ public boolean isAligned() {
+ return aligned;
+ }
+
+ public void setAligned(boolean aligned) {
+ this.aligned = aligned;
+ }
+
+ public boolean isAdditive() {
+ return additive;
+ }
+
+ public void setAdditive(boolean additive) {
+ this.additive = additive;
+ }
+
+ public boolean isBehind() {
+ return behind;
+ }
+
+ public void setBehind(boolean behind) {
+ this.behind = behind;
+ }
+
+ public boolean isPremultipliedAlpha() {
+ return premultipliedAlpha;
+ }
+
+ public void setPremultipliedAlpha(boolean premultipliedAlpha) {
+ this.premultipliedAlpha = premultipliedAlpha;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/ParticleAttributeValue.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/ParticleAttributeValue.java
new file mode 100644
index 0000000..85bb595
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/ParticleAttributeValue.java
@@ -0,0 +1,17 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public abstract class ParticleAttributeValue {
+
+ private ParticleAttributeValueType type;
+
+ public ParticleAttributeValue(ParticleAttributeValueType type){
+ this.type = type;
+ }
+
+ public ParticleAttributeValueType getType() {
+ return type;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/ParticleAttributeValueType.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/ParticleAttributeValueType.java
new file mode 100644
index 0000000..b27154b
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/ParticleAttributeValueType.java
@@ -0,0 +1,28 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public enum ParticleAttributeValueType {
+
+ RANGE, TIMELINE, TINT_TIMELINE, SPAWN_SHAPE, OPTIONS, IMAGE_PATH;
+
+ public ParticleAttributeValue createInstance(){
+ switch (this){
+ case RANGE:
+ return new Range();
+ case TIMELINE:
+ return new Timeline();
+ case TINT_TIMELINE:
+ return new TintTimeline();
+ case SPAWN_SHAPE:
+ return new SpawnShape();
+ case OPTIONS:
+ return new Options();
+ case IMAGE_PATH:
+ return new ImagePath();
+ }
+ return null;
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/Range.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/Range.java
new file mode 100644
index 0000000..f744117
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/Range.java
@@ -0,0 +1,82 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+import java.util.Random;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Range extends ParticleAttributeValue {
+
+ private float lowMin;
+ private float lowMax;
+ private float highMin;
+ private float highMax;
+
+ public Range() {
+ super(ParticleAttributeValueType.RANGE);
+ }
+
+ public Range createNormalizedInstance(Random random) {
+ Range range = new Range();
+ float high = createHighValue(random);
+ range.setHighMax(high);
+ range.setHighMin(high);
+ float low = createLowValue(random);
+ range.setLowMax(low);
+ range.setLowMin(low);
+ return range;
+ }
+
+ public float createValue(Random random, float mix) {
+ if (mix == 1)
+ return createHighValue(random);
+ else if (mix == 0)
+ return createLowValue(random);
+ else {
+ float highValue = createHighValue(random);
+ float lowValue = createLowValue(random);
+ return mix * (highValue - lowValue) + lowValue;
+ }
+ }
+
+ private float createHighValue(Random random) {
+ float min = highMin;
+ float max = highMax;
+ if (min == max)
+ return min;
+ return random.nextFloat() * (max - min) + min;
+ }
+
+ private float createLowValue(Random random) {
+ float min = lowMin;
+ float max = lowMax;
+ if (min == max)
+ return min;
+ return random.nextFloat() * (max - min) + min;
+ }
+
+ public void setLowMin(float lowMin) {
+ this.lowMin = lowMin;
+ }
+
+ public void setLowMax(float lowMax) {
+ this.lowMax = lowMax;
+ }
+
+ public void setHighMin(float highMin) {
+ this.highMin = highMin;
+ }
+
+ public void setHighMax(float highMax) {
+ this.highMax = highMax;
+ }
+
+ private boolean isLowDifference() {
+ return lowMin - lowMax != 0;
+ }
+
+ private boolean isHighDifference() {
+ return highMin - highMax != 0;
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/SpawnShape.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/SpawnShape.java
new file mode 100644
index 0000000..1e87f72
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/SpawnShape.java
@@ -0,0 +1,38 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class SpawnShape extends ParticleAttributeValue {
+
+ public enum Shape {
+ POINT("point"), SQUARE("square");
+
+ private String name;
+
+ Shape(String name) {
+ this.name = name;
+ }
+
+ public static Shape byName(String text) throws Exception{
+ for(Shape shape : values())
+ if(shape.name.equals(text))
+ return shape;
+ throw new Exception("Shape with name \""+text+"\" does not exist");
+ }
+ }
+
+ private Shape shape;
+
+ public SpawnShape() {
+ super(ParticleAttributeValueType.SPAWN_SHAPE);
+ }
+
+ public Shape getShape() {
+ return shape;
+ }
+
+ public void setShape(Shape shape) {
+ this.shape = shape;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/Timeline.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/Timeline.java
new file mode 100644
index 0000000..166a36b
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/Timeline.java
@@ -0,0 +1,52 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class Timeline extends ParticleAttributeValue {
+
+ private List points = new ArrayList<>();
+
+ public Timeline() {
+ super(ParticleAttributeValueType.TIMELINE);
+ }
+
+ public void setValueOfPoint(int index, float value) {
+ if (points.size() <= index) {
+ points.add(new TimelinePoint());
+ }
+ points.get(index).setValue(value);
+ }
+
+ public void setTimeOfPoint(int index, float time) {
+ if (points.size() <= index) {
+ points.add(new TimelinePoint());
+ }
+ points.get(index).setTime(time);
+ }
+
+ public float getValueAtTime(float time) {
+ TimelinePoint left = null, right = null;
+ for (TimelinePoint point : points) {
+ if (point.getTime() <= time) {
+ if (left == null || left.getTime() < point.getTime())
+ left = point;
+ } else if (right == null || right.getTime() > point.getTime())
+ right = point;
+ }
+ if (left != null) {
+ if (right != null) {
+ float leftDist = 1 - Math.abs(left.getTime() - time);
+ float rightDist = 1 - Math.abs(right.getTime() - time);
+ float totalDist = leftDist + rightDist;
+ return left.getValue() * (leftDist / totalDist) + right.getValue() * (rightDist / totalDist);
+ }
+ return left.getValue();
+ }
+ return 0;
+ }
+
+}
\ No newline at end of file
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TimelinePoint.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TimelinePoint.java
new file mode 100644
index 0000000..093ee65
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TimelinePoint.java
@@ -0,0 +1,23 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+public class TimelinePoint {
+
+ private float time, value;
+
+ public float getTime() {
+ return time;
+ }
+
+ public void setTime(float time) {
+ this.time = time;
+ }
+
+ public float getValue() {
+ return value;
+ }
+
+ public void setValue(float value) {
+ this.value = value;
+ }
+
+}
\ No newline at end of file
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TimelineRange.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TimelineRange.java
new file mode 100644
index 0000000..c07aea5
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TimelineRange.java
@@ -0,0 +1,34 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Range;
+import de.frajul.particlelab.entities.particles.attributes.attributeValues.Timeline;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class TimelineRange {
+
+ private Timeline timeline;
+ private Range range;
+
+ public TimelineRange(Timeline timeline, Range range) {
+ this.timeline = timeline;
+ this.range = range;
+ }
+
+ public Timeline getTimeline() {
+ return timeline;
+ }
+
+ public void setTimeline(Timeline timeline) {
+ this.timeline = timeline;
+ }
+
+ public Range getRange() {
+ return range;
+ }
+
+ public void setRange(Range range) {
+ this.range = range;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TintTimeline.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TintTimeline.java
new file mode 100644
index 0000000..e4750a3
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TintTimeline.java
@@ -0,0 +1,57 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+import de.frajul.particlelab.data.Color;
+import de.frajul.particlelab.main.GameLog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class TintTimeline extends ParticleAttributeValue {
+
+ private List points = new ArrayList<>();
+
+ public TintTimeline() {
+ super(ParticleAttributeValueType.TINT_TIMELINE);
+ }
+
+ public void setValueOfPoint(int index, int colorIndex, float value) {
+ GameLog.i("Set value: index="+index+"; colorIndex="+colorIndex+"; value="+value);
+ if (points.size() <= index) {
+ points.add(new TintTimelinePoint());
+ }
+ points.get(index).setValue(colorIndex, value);
+ }
+
+ public void setTimeOfPoint(int index, float time) {
+ GameLog.i("Set time: index="+index+"; time="+time);
+ if (points.size() <= index) {
+ points.add(new TintTimelinePoint());
+ }
+ points.get(index).setTime(time);
+ }
+
+ public Color getValueAtTime(float time) {
+ TintTimelinePoint left = null, right = null;
+ for (TintTimelinePoint point : points) {
+ if (point.getTime() <= time) {
+ if (left == null || left.getTime() < point.getTime())
+ left = point;
+ } else if (right == null || right.getTime() > point.getTime())
+ right = point;
+ }
+ if (left != null) {
+ if (right != null) {
+ float leftDist = 1 - Math.abs(left.getTime() - time);
+ float rightDist = 1 - Math.abs(right.getTime() - time);
+ float totalDist = leftDist + rightDist;
+ return left.getColor().mix((leftDist / totalDist), (rightDist / totalDist), right.getColor());
+ }
+ return left.getColor();
+ }
+ return new Color();
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TintTimelinePoint.java b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TintTimelinePoint.java
new file mode 100644
index 0000000..94bb10b
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/particles/attributes/attributeValues/TintTimelinePoint.java
@@ -0,0 +1,33 @@
+package de.frajul.particlelab.entities.particles.attributes.attributeValues;
+
+import de.frajul.particlelab.data.Color;
+
+public class TintTimelinePoint {
+
+ private float time;
+ private Color color;
+
+ public float getTime() {
+ return time;
+ }
+
+ public void setTime(float time) {
+ this.time = time;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+
+ public void setValue(int colorIndex, float value) {
+ if (color == null)
+ color = new Color();
+ if (colorIndex == 0)
+ color.setR(value);
+ else if (colorIndex == 1)
+ color.setG(value);
+ else if (colorIndex == 2)
+ color.setB(value);
+ }
+
+}
\ No newline at end of file
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/textures/Texture.java b/particlelab/src/main/java/de/frajul/particlelab/entities/textures/Texture.java
new file mode 100644
index 0000000..4ae3149
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/textures/Texture.java
@@ -0,0 +1,44 @@
+package de.frajul.particlelab.entities.textures;
+
+/**
+ * Created by Julian on 11.12.2015.
+ */
+public class Texture {
+
+ private int id;
+ private int atlasWidth;
+ private int atlasHeight;
+ private int atlasIndex;
+
+ public Texture(int id, int atlasWidth, int atlasHeight) {
+ this.id = id;
+ this.atlasWidth = atlasWidth;
+ this.atlasHeight = atlasHeight;
+ }
+
+ public Texture(Texture other) {
+ this.id = other.getId();
+ this.atlasWidth = other.getAtlasWidth();
+ this.atlasHeight = other.getAtlasHeight();
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public int getAtlasWidth() {
+ return atlasWidth;
+ }
+
+ public int getAtlasHeight() {
+ return atlasHeight;
+ }
+
+ public int getAtlasIndex() {
+ return atlasIndex;
+ }
+
+ public void setAtlasIndex(int atlasIndex) {
+ this.atlasIndex = atlasIndex;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/textures/TextureLoader.java b/particlelab/src/main/java/de/frajul/particlelab/entities/textures/TextureLoader.java
new file mode 100644
index 0000000..017b22b
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/textures/TextureLoader.java
@@ -0,0 +1,62 @@
+package de.frajul.particlelab.entities.textures;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+
+import de.frajul.particlelab.main.GameLog;
+
+import java.io.InputStream;
+
+/**
+ * Created by Julian on 26.11.2015.
+ */
+public class TextureLoader {
+
+ private Context context;
+
+ public TextureLoader(Context context) {
+ this.context = context;
+ }
+
+ public int loadTextureId(int texture, boolean isAtlas) {
+ Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), texture);
+ return loadTextureId(bitmap, isAtlas);
+ }
+
+ public Texture loadTexture(String inAssetsLocation) throws Exception {
+ InputStream is = context.getAssets().open(inAssetsLocation);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inScaled = false;
+ Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
+ return new Texture(loadTextureId(bitmap, false), 1, 1);
+ }
+
+ private int loadTextureId(Bitmap bitmap, boolean isAtlas) {
+ int id = genTexture();
+
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id);
+ if (!isAtlas) {
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ } else {
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ }
+
+ GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
+
+ bitmap.recycle();
+ GameLog.d("Texture " + id + " successfully loaded");
+ return id;
+ }
+
+ private int genTexture() {
+ int[] idField = new int[1];
+ GLES20.glGenTextures(1, idField, 0);
+ return idField[0];
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/entities/textures/TexturePack.java b/particlelab/src/main/java/de/frajul/particlelab/entities/textures/TexturePack.java
new file mode 100644
index 0000000..e2d2667
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/entities/textures/TexturePack.java
@@ -0,0 +1,35 @@
+package de.frajul.particlelab.entities.textures;
+
+import android.content.Context;
+
+import de.frajul.particlelab.R;
+
+/**
+ * Created by Julian on 05.12.2015.
+ */
+public class TexturePack {
+
+ private TextureLoader loader;
+
+ public final Texture star;
+ public final Texture yellowParticle;
+ public final Texture redParticle;
+
+ public TexturePack(Context context) {
+ loader = new TextureLoader(context);
+ star = loadTexture(R.drawable.star);
+
+ yellowParticle = loadTexture(R.drawable.yellowparticle);
+ redParticle = loadTexture(R.drawable.redparticle);
+ }
+
+ private Texture loadTexture(int id) {
+ int texId = loader.loadTextureId(id, false);
+ return new Texture(texId, 1, 1);
+ }
+
+ public Texture loadAtlas(int id, int atlasWidth, int atlasHeight) {
+ int texId = loader.loadTextureId(id, true);
+ return new Texture(texId, atlasWidth, atlasHeight);
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/main/GameLog.java b/particlelab/src/main/java/de/frajul/particlelab/main/GameLog.java
new file mode 100644
index 0000000..050751c
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/main/GameLog.java
@@ -0,0 +1,44 @@
+package de.frajul.particlelab.main;
+
+import android.util.Log;
+
+/**
+ * Created by Julian on 23.11.2015.
+ */
+public class GameLog {
+
+ private final static String TAG = "GameLog";
+ public static boolean debugging = true;
+
+ public static void i(String message) {
+ Log.i(TAG + getCallerInfo(), message);
+ }
+
+ public static void d(String message) {
+ if (debugging)
+ Log.d(TAG + getCallerInfo(), message);
+ }
+
+ public static void e(String message) {
+ Log.e(TAG + getCallerInfo(), message);
+ }
+
+ public static void e(Throwable error) {
+ Log.e(TAG + getCallerInfo(), error.getMessage(), error);
+ }
+
+ //Possible to get Method which called i, d, e
+ //Method found at stack[4]
+ public static void stack() {
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+ Log.i(TAG + "Stack", "StackSize: " + stack.length);
+ for (int i = 0; i < stack.length; i++) {
+ Log.i(TAG + "Stack", i + ": " + stack[i]);
+ }
+ }
+
+ private static String getCallerInfo() {
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+ return "(" + stack[4].getFileName() + ", " + stack[4].getMethodName() + ", " + stack[4].getLineNumber() + ")";
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/main/MainActivity.java b/particlelab/src/main/java/de/frajul/particlelab/main/MainActivity.java
new file mode 100644
index 0000000..b52e1e7
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/main/MainActivity.java
@@ -0,0 +1,81 @@
+package de.frajul.particlelab.main;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.Window;
+import android.view.WindowManager;
+
+import de.frajul.particlelab.entities.particles.ParticleReader;
+import de.frajul.particlelab.entities.particles.ParticleSystem;
+import de.frajul.particlelab.rendering.GameRenderer;
+import de.frajul.particlelab.rendering.Rendering;
+
+/**
+ * Created by Julian on 02.08.2016.
+ */
+public class MainActivity extends Activity {
+
+ private MyGlSurfaceView glSurfaceView;
+ private ParticleSystem particleSystem;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ try {
+ GameLog.d("OnCreate");
+ super.onCreate(savedInstanceState);
+ super.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ super.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ if (!hasGLES20())
+ throw new Exception("OpenGL ES 2.0 not supported");
+
+ particleSystem = new ParticleSystem(this);
+ Rendering rendering = new Rendering(this, particleSystem);
+ glSurfaceView = new MyGlSurfaceView(this, new GameRenderer(this,rendering));
+ super.setContentView(glSurfaceView);
+ } catch (Exception e) {
+ onException(e);
+ }
+ }
+
+ public void onException(Exception e) {
+ GameLog.e(e);
+ super.finish();
+ }
+
+ @Override
+ protected void onPause() {
+ GameLog.d("OnPause");
+ glSurfaceView.onPause();
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ GameLog.d("OnResume");
+ glSurfaceView.onResume();
+ super.onResume();
+ }
+
+ @Override
+ protected void onDestroy() {
+ GameLog.d("OnDestroy");
+ super.onDestroy();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+ private boolean hasGLES20() {
+ ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ ConfigurationInfo info = am.getDeviceConfigurationInfo();
+ return info.reqGlEsVersion >= 0x20000;
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/main/MyGlSurfaceView.java b/particlelab/src/main/java/de/frajul/particlelab/main/MyGlSurfaceView.java
new file mode 100644
index 0000000..8062b48
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/main/MyGlSurfaceView.java
@@ -0,0 +1,35 @@
+package de.frajul.particlelab.main;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+
+import de.frajul.particlelab.rendering.GameRenderer;
+
+/**
+ * Created by Julian on 30.07.2016.
+ */
+public class MyGlSurfaceView extends GLSurfaceView {
+
+ private boolean rendererSet;
+
+ public MyGlSurfaceView(Context context, GameRenderer gameRenderer) throws Exception {
+ super(context);
+ super.setEGLContextClientVersion(2);
+ super.setRenderer(gameRenderer);
+ rendererSet = true;
+ }
+
+ @Override
+ public void onResume() {
+ GameLog.i("SurfaceView: onResume");
+ if (rendererSet)
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ GameLog.i("SurfaceView: onPause");
+ if (rendererSet)
+ super.onPause();
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/main/Scene.java b/particlelab/src/main/java/de/frajul/particlelab/main/Scene.java
new file mode 100644
index 0000000..8247b3a
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/main/Scene.java
@@ -0,0 +1,69 @@
+package de.frajul.particlelab.main;
+
+import android.content.Context;
+
+import de.frajul.particlelab.data.Vector;
+import de.frajul.particlelab.entities.Entity;
+import de.frajul.particlelab.entities.particles.ParticleSource;
+import de.frajul.particlelab.entities.particles.ParticleSystem;
+import de.frajul.particlelab.entities.textures.TexturePack;
+import de.frajul.particlelab.rendering.Timer;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Created by Julian on 20.07.2016.
+ */
+public class Scene extends ArrayList {
+
+ public float cameraX;
+
+ private Vector screenSize;
+
+ private ParticleSystem particleSystem;
+ private TexturePack textures;
+
+ public Scene(Context context, TexturePack texturePack, ParticleSystem particleSystem) {
+ this.particleSystem = particleSystem;
+ setTexturePack(texturePack);
+ new ParticleSource(new Vector(0, 0), particleSystem.firework).start();
+ }
+
+ public void setTexturePack(TexturePack texturePack) {
+ this.textures = texturePack;
+
+ }
+
+ public void update(Timer timer) {
+ Iterator iterator = super.iterator();
+ while (iterator.hasNext()) {
+ Entity entity = iterator.next();
+ Vector movement = entity.getMovement();
+ Vector finalMovement = new Vector(movement).mul(timer.getFrameTime());
+ entity.move(finalMovement);
+ if (entity.getRightEdge() - cameraX < -3f) {
+ iterator.remove();
+ super.remove(entity);
+ }
+ }
+ }
+
+ public Vector calcWorldFromScreenCoords(float screenX, float screenY) throws Exception {
+ if (screenSize == null)
+ throw new Exception("ScreenSize not set");
+ float glCoordWidth = (2f * screenSize.x / screenSize.y);
+ float x = ((screenX / screenSize.x) * 2f - 1f) * glCoordWidth / 2;
+ x += cameraX;
+ float y = -((screenY / screenSize.y) * 2f - 1f);
+ return new Vector(x, y);
+ }
+
+ public void setScreenSize(Vector screenSize) {
+ this.screenSize = screenSize;
+ }
+
+ public ParticleSystem getParticleSystem() {
+ return particleSystem;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/rendering/GameRenderer.java b/particlelab/src/main/java/de/frajul/particlelab/rendering/GameRenderer.java
new file mode 100644
index 0000000..90416b0
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/rendering/GameRenderer.java
@@ -0,0 +1,157 @@
+package de.frajul.particlelab.rendering;
+
+import android.content.Context;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+
+import de.frajul.particlelab.data.Color;
+import de.frajul.particlelab.entities.Entity;
+import de.frajul.particlelab.entities.particles.ParticleEffect;
+import de.frajul.particlelab.entities.particles.ParticleSource;
+import de.frajul.particlelab.entities.particles.ParticleSystem;
+import de.frajul.particlelab.entities.textures.TexturePack;
+import de.frajul.particlelab.main.GameLog;
+import de.frajul.particlelab.main.Scene;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Created by Julian on 22.11.2015.
+ */
+public class GameRenderer implements GLSurfaceView.Renderer {
+
+ private Rendering rendering;
+ private MatrixCreator matrixCreator;
+ private boolean additiveBlending;
+
+ private Color emptyColor;
+ private Context context;
+ private Quad quad;
+ //GL Context
+ private ShaderProgram shaderProgram;
+ private TexturePack texturePack;
+ private Timer timer;
+
+ public GameRenderer(Context context, Rendering rendering) {
+ this.context = context;
+ this.rendering = rendering;
+ emptyColor = new Color(-1, -1, -1);
+ matrixCreator = new MatrixCreator();
+ quad = new Quad();
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ GameLog.d("onSurfaceCreated");
+ GLES20.glClearColor(0, 0, 0, 1.0f);
+ gl.glEnable(GL10.GL_BLEND);
+ gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
+ try {
+ shaderProgram = new ShaderProgram(context);
+ texturePack = new TexturePack(context);
+ timer = new Timer();
+ } catch (Exception e) {
+ rendering.onException(e);
+ }
+ rendering.initiate(texturePack, timer);
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ GameLog.d("onSurfaceChanged: width=" + width + ", height=" + height);
+ GLES20.glViewport(0, 0, width, height);
+ matrixCreator.setMVPMSize(width, height);
+ rendering.setScreenSize(width, height);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ timer.update();
+ rendering.update();
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+
+ Scene scene = rendering.getScene();
+ ParticleSystem particleSystem = scene.getParticleSystem();
+
+// GLES20.glClearColor(0, 0, 0, 0.0f);
+// GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+//
+// shaderProgram.start();
+// shaderProgram.loadMVPMatrix(matrixCreator, scene.cameraX);
+// synchronized (scene.getParticleSystem().getEffects()) {
+// for (ParticleEffect effect : scene.getParticleSystem().getEffects()) {
+// gl.glActiveTexture(GL10.GL_TEXTURE0);
+// gl.glBindTexture(GL10.GL_TEXTURE_2D, effect.getTexture().getId());
+// shaderProgram.loadTextureAtlasInfos(effect.getTexture());
+//
+// switchAdditiveBlending(gl, effect.getOptions().isAdditive());
+// synchronized (effect.getSources()) {
+// for (ParticleSource source : effect.getSources()) {
+// source.getActiveParticleLock().lock();
+// for (Entity particle : source.getActiveParticles())
+// renderParticle(gl, particle);
+// source.getActiveParticleLock().unlock();
+// }
+// }
+// }
+// }
+// disableAdditiveBlending(gl);
+//
+// shaderProgram.stop();
+
+ shaderProgram.start();
+ shaderProgram.loadMVPMatrix(matrixCreator, scene.cameraX);
+ for (ParticleEffect effect : particleSystem.getEffects()) {
+ gl.glActiveTexture(GL10.GL_TEXTURE0);
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, effect.getTexture().getId());
+ shaderProgram.loadTextureAtlasInfos(effect.getTexture());
+
+ if (effect.getOptions().isAdditive() && !additiveBlending)
+ enableAdditiveBlending(gl);
+ if (!effect.getOptions().isAdditive() && additiveBlending)
+ disableAdditiveBlending(gl);
+ for (ParticleSource source : effect.getSources()) {
+ source.getActiveParticleLock().lock();
+ for (Entity particle : source.getActiveParticles())
+ renderParticle(gl, particle);
+ source.getActiveParticleLock().unlock();
+ }
+ }
+ shaderProgram.stop();
+ }
+
+ private void switchAdditiveBlending(GL10 gl, boolean effectAdditive) {
+ if (effectAdditive && !additiveBlending)
+ enableAdditiveBlending(gl);
+ if (effectAdditive && additiveBlending)
+ disableAdditiveBlending(gl);
+ }
+
+ private void renderEntity(GL10 gl, Entity entity) {
+ gl.glActiveTexture(GL10.GL_TEXTURE0);
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, entity.getTexture().getId());
+ shaderProgram.loadTransformationMatrix(matrixCreator, entity);
+ shaderProgram.loadAlpha(entity.getAlpha());
+ shaderProgram.loadTextureAtlasInfos(entity.getTexture());
+ shaderProgram.loadColor(entity.getColor() != null ? entity.getColor() : emptyColor);
+ quad.draw();
+ }
+
+ private void renderParticle(GL10 gl, Entity entity) {
+ shaderProgram.loadTransformationMatrix(matrixCreator, entity);
+ shaderProgram.loadAlpha(entity.getAlpha());
+ shaderProgram.loadColor(entity.getColor() != null ? entity.getColor() : emptyColor);
+ quad.draw();
+ }
+
+ private void enableAdditiveBlending(GL10 gl) {
+ additiveBlending = true;
+ gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
+ }
+
+ private void disableAdditiveBlending(GL10 gl) {
+ additiveBlending = false;
+ gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/rendering/Lock.java b/particlelab/src/main/java/de/frajul/particlelab/rendering/Lock.java
new file mode 100644
index 0000000..d70058d
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/rendering/Lock.java
@@ -0,0 +1,22 @@
+package de.frajul.particlelab.rendering;
+
+public class Lock {
+
+ private boolean isLocked = false;
+
+ public synchronized void lock() {
+ while (isLocked) {
+ try {
+ wait();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ isLocked = true;
+ }
+
+ public synchronized void unlock() {
+ isLocked = false;
+ notify();
+ }
+}
\ No newline at end of file
diff --git a/particlelab/src/main/java/de/frajul/particlelab/rendering/MatrixCreator.java b/particlelab/src/main/java/de/frajul/particlelab/rendering/MatrixCreator.java
new file mode 100644
index 0000000..874d5c0
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/rendering/MatrixCreator.java
@@ -0,0 +1,49 @@
+package de.frajul.particlelab.rendering;
+
+import android.opengl.Matrix;
+
+import de.frajul.particlelab.entities.Entity;
+import de.frajul.particlelab.data.Vector;
+
+/**
+ * Created by Julian on 23.11.2015.
+ */
+public class MatrixCreator {
+
+ private float width, height;
+
+ public void setMVPMSize(float width, float height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ public float[] createModelViewProjectionMatrix(float cameraX) {
+ float[] mvpMatrix = new float[16];
+ float[] projectionMatrix = new float[16];
+ float[] viewMatrix = new float[16];
+
+ float ratio = width / height;
+ Matrix.frustumM(projectionMatrix, 0, -ratio + cameraX, ratio + cameraX, -1, 1, 1, 2);
+ Matrix.setLookAtM(viewMatrix, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0);
+ Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
+ return mvpMatrix;
+ }
+
+ public float[] createTransformationMatrix(Entity entity) {
+ float width = entity.getWidth();
+ float height = entity.getHeight();
+ float rotation = entity.getRotation();
+ Vector position = entity.getPosition();
+ return createTransformationMatrix(width, height, rotation, position);
+ }
+
+ public float[] createTransformationMatrix(float width, float height, float rotation, Vector position) {
+ float[] transformationMatrix = new float[16];
+ Matrix.setIdentityM(transformationMatrix, 0);
+ Matrix.translateM(transformationMatrix, 0, position.x, position.y, 0);
+ Matrix.rotateM(transformationMatrix, 0, rotation, 0, 0, 1);
+ Matrix.scaleM(transformationMatrix, 0, width * .5f, height * .5f, 0);
+ return transformationMatrix;
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/rendering/Quad.java b/particlelab/src/main/java/de/frajul/particlelab/rendering/Quad.java
new file mode 100644
index 0000000..04ad520
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/rendering/Quad.java
@@ -0,0 +1,49 @@
+package de.frajul.particlelab.rendering;
+
+import android.opengl.GLES20;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * Created by Julian on 26.11.2015.
+ */
+public class Quad {
+
+ private FloatBuffer vertexBuffer;
+ private FloatBuffer textureBuffer;
+
+ private float vertices[] = {1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
+ 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f};
+
+ private float textures[] = {1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f};
+
+ public Quad() {
+ vertexBuffer = createEmptyFloatBuffer();
+ vertexBuffer.put(vertices);
+ vertexBuffer.position(0);
+
+ textureBuffer = createEmptyFloatBuffer();
+ textureBuffer.put(textures);
+ textureBuffer.position(0);
+ }
+
+ private FloatBuffer createEmptyFloatBuffer() {
+ ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
+ bb.order(ByteOrder.nativeOrder());
+ return bb.asFloatBuffer();
+ }
+
+ public void draw() {
+ GLES20.glEnableVertexAttribArray(0);
+ GLES20.glEnableVertexAttribArray(1);
+ GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer);
+ GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertices.length / 2);
+ GLES20.glDisableVertexAttribArray(1);
+ GLES20.glDisableVertexAttribArray(0);
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/rendering/Rendering.java b/particlelab/src/main/java/de/frajul/particlelab/rendering/Rendering.java
new file mode 100644
index 0000000..3110c5d
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/rendering/Rendering.java
@@ -0,0 +1,57 @@
+package de.frajul.particlelab.rendering;
+
+import de.frajul.particlelab.entities.particles.ParticleSystem;
+import de.frajul.particlelab.entities.textures.TexturePack;
+import de.frajul.particlelab.main.MainActivity;
+import de.frajul.particlelab.main.Scene;
+import de.frajul.particlelab.data.Vector;
+
+/**
+ * Created by Julian on 26.11.2015.
+ */
+public class Rendering {
+
+ private MainActivity mainActivity;
+ private ParticleSystem particleSystem;
+ private Timer timer;
+ private Scene scene;
+ private boolean alreadyInitiated = false;
+
+ public Rendering(MainActivity mainActivity, ParticleSystem particleSystem) {
+ this.mainActivity = mainActivity;
+ this.particleSystem = particleSystem;
+ }
+
+ public void initiate(TexturePack texturePack, Timer timer) {
+ this.timer = timer;
+ if(!alreadyInitiated)
+ scene = new Scene(mainActivity, texturePack, particleSystem);
+ else {
+ scene.setTexturePack(texturePack);
+ }
+ try {
+ particleSystem.loadTextures();
+ }catch (Exception e){
+ onException(e);
+ }
+ alreadyInitiated = true;
+ }
+
+ public void update() {
+ particleSystem.update(timer);
+ scene.update(timer);
+ }
+
+ public void setScreenSize(int width, int height) {
+ if (scene != null)
+ scene.setScreenSize(new Vector(width, height));
+ }
+
+ public void onException(Exception e) {
+ mainActivity.onException(e);
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/rendering/ShaderProgram.java b/particlelab/src/main/java/de/frajul/particlelab/rendering/ShaderProgram.java
new file mode 100644
index 0000000..7d115db
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/rendering/ShaderProgram.java
@@ -0,0 +1,138 @@
+package de.frajul.particlelab.rendering;
+
+import android.content.Context;
+import android.opengl.GLES20;
+
+import de.frajul.particlelab.entities.Entity;
+import de.frajul.particlelab.data.Color;
+import de.frajul.particlelab.entities.textures.Texture;
+import de.frajul.particlelab.main.GameLog;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * Created by Julian on 23.11.2015.
+ */
+public class ShaderProgram {
+
+ private Context context;
+ private int vertexShader, fragmentShader, program;
+ private int location_mvpMatrix;
+ private int location_transformationMatrix;
+ private int location_alpha;
+ private int location_texAtlasSize;
+ private int location_texAtlasIndex;
+ private int location_isTerrain;
+ private int location_color;
+
+ public ShaderProgram(Context context) throws Exception {
+ this.context = context;
+ vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, "vertexShader.glsl");
+ fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, "fragmentShader.glsl");
+ program = GLES20.glCreateProgram();
+ GLES20.glAttachShader(program, vertexShader);
+ GLES20.glAttachShader(program, fragmentShader);
+ GLES20.glLinkProgram(program);
+ int[] linkStatus = new int[1];
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+ if (linkStatus[0] == GLES20.GL_FALSE) {
+ GLES20.glDeleteProgram(program);
+ throw new Exception("Could not link program: "
+ + GLES20.glGetProgramInfoLog(program));
+ }
+
+ bindAttribLocations();
+ loadUniformLocations();
+ GameLog.d("ShaderProgram successfully loaded");
+ }
+
+ private void loadUniformLocations() {
+ location_mvpMatrix = GLES20.glGetUniformLocation(program, "mvpMatrix");
+ location_transformationMatrix = GLES20.glGetUniformLocation(program, "transformationMatrix");
+ location_alpha = GLES20.glGetUniformLocation(program, "alpha");
+ location_texAtlasSize = GLES20.glGetUniformLocation(program, "texAtlasSize");
+ location_texAtlasIndex = GLES20.glGetUniformLocation(program, "texAtlasIndex");
+ location_isTerrain = GLES20.glGetUniformLocation(program, "isTerrain");
+ location_color = GLES20.glGetUniformLocation(program, "color");
+ }
+
+ private void bindAttribLocations() {
+ GLES20.glBindAttribLocation(program, 0, "position");
+ GLES20.glBindAttribLocation(program, 1, "texCoords");
+ }
+
+ public void start() {
+ GLES20.glUseProgram(program);
+ }
+
+ public void stop() {
+ GLES20.glUseProgram(0);
+ }
+
+ public void loadMVPMatrix(MatrixCreator matrixCreator, float cameraX) {
+ float[] mvpMatrix = matrixCreator.createModelViewProjectionMatrix(cameraX);
+ GLES20.glUniformMatrix4fv(location_mvpMatrix, 1, false, mvpMatrix, 0);
+ }
+
+ public void loadTransformationMatrix(MatrixCreator matrixCreator, Entity entity) {
+ float[] transformationMatrix = matrixCreator.createTransformationMatrix(entity);
+ GLES20.glUniformMatrix4fv(location_transformationMatrix, 1, false, transformationMatrix, 0);
+ }
+
+ public void loadColor(Color color){
+ GLES20.glUniform3f(location_color, color.getR(), color.getG(), color.getB());
+ }
+
+ public void loadAlpha(float alpha) {
+ GLES20.glUniform1f(location_alpha, alpha);
+ }
+
+ public void loadTextureAtlasInfos(Texture texture) {
+ GLES20.glUniform2f(location_texAtlasSize, texture.getAtlasWidth(), texture.getAtlasHeight());
+ GLES20.glUniform1f(location_texAtlasIndex, texture.getAtlasIndex());
+ }
+
+ public void loadIsTerrain(boolean isTerrain) {
+ float fIsTerrain = isTerrain ? 1 : 0;
+ GLES20.glUniform1f(location_isTerrain, fIsTerrain);
+ }
+
+ public void cleanUp() {
+ stop();
+ GLES20.glDeleteShader(vertexShader);
+ GLES20.glDeleteShader(fragmentShader);
+ GLES20.glDeleteProgram(program);
+ GameLog.d("Shader cleaned");
+ }
+
+ private int loadShader(int type, String shaderName) throws Exception {
+ try {
+ InputStream is = context.getAssets().open(shaderName);
+ InputStreamReader isReader = new InputStreamReader(is);
+ BufferedReader reader = new BufferedReader(isReader);
+ StringBuilder source = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null)
+ source.append(line);
+
+ int shader = GLES20.glCreateShader(type);
+ GLES20.glShaderSource(shader, source.toString());
+ GLES20.glCompileShader(shader);
+
+ int[] compiled = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+ if (compiled[0] == GLES20.GL_FALSE) {
+ GLES20.glDeleteShader(shader);
+ throw new Exception("Could not compile shader \"" + shaderName + "\": "
+ + GLES20.glGetShaderInfoLog(shader));
+ }
+ GameLog.d("Shader \"" + shaderName + "\" successfully loaded");
+ return shader;
+ } catch (Exception e) {
+ throw new Exception("Could not load Shader \"" + shaderName + "\"", e);
+ }
+ }
+
+}
diff --git a/particlelab/src/main/java/de/frajul/particlelab/rendering/Timer.java b/particlelab/src/main/java/de/frajul/particlelab/rendering/Timer.java
new file mode 100644
index 0000000..7258acf
--- /dev/null
+++ b/particlelab/src/main/java/de/frajul/particlelab/rendering/Timer.java
@@ -0,0 +1,46 @@
+package de.frajul.particlelab.rendering;
+
+/**
+ * Created by Julian on 22.11.2015.
+ */
+public class Timer {
+
+ private long lastFpsTime;
+ private int fpsCounter;
+ private int fps;
+
+ private long lastTime;
+ private long delta;
+
+
+ public Timer() {
+ lastTime = System.currentTimeMillis();
+ lastFpsTime = lastTime;
+ }
+
+ public void update() {
+ long currentTime = System.currentTimeMillis();
+ delta = currentTime - lastTime;
+ lastTime = currentTime;
+
+ fpsCounter++;
+ if (currentTime - lastFpsTime > 1000) {
+ fps = fpsCounter;
+ lastFpsTime += 1000;
+ fpsCounter = 0;
+ }
+ }
+
+ public float getFrameTime() {
+ return delta;
+ }
+
+ public int getFps() {
+ return fps;
+ }
+
+ public long getCurrentTime() {
+ return System.currentTimeMillis();
+ }
+
+}