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 collection) { + return super.addAll(collection); + } + + @Override + public synchronized boolean addAll(int index, Collection 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 list, Timer timer){ + synchronized (list){ + Iterator 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 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(); + } + +}