diff --git a/.gitignore b/.gitignore
index a8b0d1d..d3c6c1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,45 @@ google-services.json
# Android Profiling
*.hprof
+## Suggested ^^
+
+## From TicTacToe project for good measure. vv
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Log/OS Files
+*.log
+
+# Android Studio generated files and folders
+captures/
+.externalNativeBuild/
+.cxx/
+*.apk
+output.json
+
+# IntelliJ
+*.iml
+.idea/
+misc.xml
+deploymentTargetDropDown.xml
+render.experimental.xml
+
+# Keystore files
+*.jks
+*.keystore
+
+# Google Services (e.g. APIs or Firebase)
+google-services.json
+
+# Android Profiling
+*.hprof
+/app/debug/output-metadata.json
+
+# Ha!
+keystore/*
+release
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100755
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100755
index 0000000..13f56a9
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 24
+ buildToolsVersion "24.0.2"
+ defaultConfig {
+ applicationId "apps.hyperling.com.platformer"
+ minSdkVersion 15
+ targetSdkVersion 24
+ versionCode 10
+ versionName "0.9"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:24.2.1'
+ compile 'com.google.android.gms:play-services-ads:9.8.0'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100755
index 0000000..2141ffd
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/ling/Android/Sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/app/src/androidTest/java/com/hyperling/apps/games/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/hyperling/apps/games/ExampleInstrumentedTest.java
new file mode 100755
index 0000000..85a88c5
--- /dev/null
+++ b/app/src/androidTest/java/com/hyperling/apps/games/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.hyperling.apps.games;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("apps.hyperling.com.platformer", appContext.getPackageName());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100755
index 0000000..f8ea237
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/hyperling/apps/games/AdsHelper.java b/app/src/main/java/com/hyperling/apps/games/AdsHelper.java
new file mode 100755
index 0000000..ed50f9b
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/AdsHelper.java
@@ -0,0 +1,36 @@
+package com.hyperling.apps.games;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.view.View;
+
+import com.google.android.gms.ads.AdRequest;
+import com.google.android.gms.ads.AdView;
+import com.google.android.gms.ads.MobileAds;
+
+/**
+ * Created by ling on 12/26/16.
+ */
+
+public class AdsHelper {
+
+ // To be called in onResume in Activities
+ public static void toggleAds(SharedPreferences sharedPreferences, String adsKey, Context applicationContext, AdView mAdView){
+ // Enable ads?
+ boolean adsEnabled = sharedPreferences.getBoolean(adsKey, false);
+ if (adsEnabled) {
+ mAdView.setVisibility(View.VISIBLE);
+ MobileAds.initialize(applicationContext, "ca-app-pub-3940256099942544~3347511713");
+ AdRequest adRequest = new AdRequest.Builder()
+ .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
+ .addTestDevice("C6A494DC6E7C9AC29102694D48487084") // Nexus 7
+ .addTestDevice("6545BCC9B60A394005546783687339B7") // Moto G
+ .build();
+ mAdView.loadAd(adRequest);
+ }
+ else{
+ mAdView.setVisibility(View.GONE);
+ }
+
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/Animation.java b/app/src/main/java/com/hyperling/apps/games/Animation.java
new file mode 100755
index 0000000..70ef76c
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/Animation.java
@@ -0,0 +1,54 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+
+/**
+ * Created by ling on 12/22/16.
+ */
+
+public class Animation {
+
+ private Bitmap[] frames;
+ private int currentFrame;
+ private long startTime;
+ private long delay;
+ private boolean playedOnce;
+
+ public void setFrames(Bitmap[] f) {
+ frames = f;
+ }
+
+ public void setDelay(long d) {
+ delay = d;
+ }
+
+ public void setCurrentFrame(int cf) {
+ currentFrame = cf;
+ }
+
+ public Bitmap getImage(){
+ return frames[currentFrame];
+ }
+
+ public int getCurrentFrame(){
+ return currentFrame;
+ }
+
+ public boolean getPlayedOnce(){
+ return playedOnce;
+ }
+
+ public void update(){
+ long elapsed = System.currentTimeMillis() - startTime;
+ if (elapsed > delay){
+ currentFrame++;
+ startTime = System.currentTimeMillis();
+ }
+
+ if (currentFrame == frames.length){
+ currentFrame = 0;
+ playedOnce = true;
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/Bird.java b/app/src/main/java/com/hyperling/apps/games/Bird.java
new file mode 100755
index 0000000..3d69a31
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/Bird.java
@@ -0,0 +1,76 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+
+/**
+ * Created by ling on 12/23/16.
+ */
+
+public class Bird extends Player {
+
+ private boolean flapping = false;
+
+ private int lives = 0;
+
+ public Bird(Bitmap b, int w, int h, int numFrames){
+ super(b, w, h, numFrames);
+ }
+
+ public boolean isFlapping() {
+ return flapping;
+ }
+
+ public void setFlapping(boolean flapping) {
+ this.flapping = flapping;
+ }
+
+ public int getLives() {
+ return lives;
+ }
+
+ public void addLives(int lives) {
+ this.lives += lives;
+ }
+
+ public void subtractLives(int lives) {
+ this.lives -= lives;
+ }
+
+ @Override
+ public void update() {
+ // Go up!
+ if (flapping){
+ dy += -1;
+ }
+ // Otherwise fall
+ else{
+ dy += 2;
+ }
+
+ // Do not fly up faster than this
+ if (dy < -5){
+ dy = -5;
+ }
+ // Do not fall faster than this
+ if (dy > 7){
+ dy = 7;
+ }
+
+ /***** Map limits *****/
+ if (y < GameView.HEIGHT - getHeight() || dy < 0) {
+ y += dy;
+ }
+ // Do not leave bottom of map
+ else{
+ y = GameView.HEIGHT - getHeight();
+ dy = 0;
+ }
+
+ // Do not leave top of map
+ if (y < 0){
+ y = 0;
+ }
+
+ super.update();
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/BirdEnemy.java b/app/src/main/java/com/hyperling/apps/games/BirdEnemy.java
new file mode 100755
index 0000000..9cef084
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/BirdEnemy.java
@@ -0,0 +1,71 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+/**
+ * Created by ling on 12/26/16.
+ */
+
+public class BirdEnemy extends GameObject {
+
+ protected Bitmap image;
+
+ private Animation animation;
+
+ private int speed, damage;
+
+ public BirdEnemy(Bitmap b, int w, int h, int numFrames, int x, int y, int speed, int damage){
+ image = b;
+ width = w;
+ height = h;
+ this.x = x;
+ this.y = y;
+ this.speed = speed;
+ this.damage = damage;
+
+ if (speed > -1){
+ speed = -1;
+ }
+ if (this.y >= GameView.HEIGHT - this.height){
+ this.y = GameView.HEIGHT - this.height;
+ }
+
+ // Create animation
+ Bitmap[] image = new Bitmap[numFrames];
+ animation = new Animation();
+
+ for (int i = 0; i < numFrames; i++){
+ image[i] = Bitmap.createBitmap(b, width * i, 0, width, height);
+ }
+
+ animation.setFrames(image);
+
+ //animation.setDelay(100);
+ int delay = 25 * Math.abs(speed);
+ if (delay > 125){
+ delay = 125;
+ }
+ else if (delay < 75){
+ delay = 75;
+ }
+ animation.setDelay(delay);
+ animation.setCurrentFrame((int)(Math.random() * numFrames));
+ }
+
+ public void update(){
+ animation.update();
+
+ // Move the object
+ x += speed;
+ }
+
+ public void draw(Canvas canvas) {
+ // Draw the object
+ canvas.drawBitmap(animation.getImage(), getX(), getY(), null);
+ }
+
+ public int getDamage() {
+ return damage;
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/BirdGameView.java b/app/src/main/java/com/hyperling/apps/games/BirdGameView.java
new file mode 100755
index 0000000..60c2063
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/BirdGameView.java
@@ -0,0 +1,290 @@
+package com.hyperling.apps.games;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+
+import java.util.ArrayList;
+
+/**
+ * Created by ling on 12/28/16.
+ */
+
+public class BirdGameView extends GameView {
+ private final String birdScoreKey;
+
+ private final int birdGameSpeed = -8;
+ private final int birdEnemySpeedVariance = 5;
+ private final int birdEnemySpeedConstant = 2;
+ private final int cloudsSpeed = birdGameSpeed/2;
+
+ private Bird bird;
+ private Bitmap birdBitmap;
+
+ private ArrayList birdEnemies;
+ private Bitmap birdEnemyBitmap;
+
+ private final int birdWidth = 30;
+ private final int birdHeight = 20;
+ private final int birdFrames = 4;
+
+ private ArrayList birdExtraLives;
+ private Bitmap birdExtraLifeBitmap;
+
+ private final int birdExtraLifeWidth = 20;
+ private final int birdExtraLifeHeight = 30;
+
+ private final int birdMaxNumEnemyRows = HEIGHT / birdHeight;
+ private final int birdEnemySpawnRate = 25;
+
+ private Bitmap backgroundBitmap, cloudsBitmap;
+
+ public BirdGameView(Context context){
+ super(context);
+
+ // Keys
+ birdScoreKey = resources.getString(R.string.birdScoreKey);
+ }
+
+ private void displayScore(Canvas canvas){
+ int highScore = sharedPreferences.getInt(birdScoreKey, 0);
+ textPaint.setColor(Color.parseColor("#330066"));
+ canvas.drawText("High: " + highScore, 0, textPaint.getTextSize(), textPaint);
+ canvas.drawText("Current: " + bird.getScore(), 0, textPaint.getTextSize() * 2, textPaint);
+
+ if (bird.getLives() > 0) {
+ canvas.drawText("Lives: " + bird.getLives(), 0, textPaint.getTextSize() * 3, textPaint);
+ }
+
+ if (fpsEnabled){
+ canvas.drawText("FPS: " + thread.averageFPS, 0, HEIGHT - 1, textPaint);
+ }
+
+ if (!bird.isPlaying()) {
+ textPaint.setColor(Color.parseColor("#330066"));
+ canvas.drawText("You are the Purple Bird", WIDTH / 4, HEIGHT / 4 - textPaint.getTextSize(), textPaint);
+
+ textPaint.setColor(Color.parseColor("#FF9900"));
+ canvas.drawText("Tap the Screen to Start", WIDTH / 4, HEIGHT / 4, textPaint);
+ canvas.drawText("Hold the Screen to Fly Higher", WIDTH / 4, HEIGHT / 4 + textPaint.getTextSize(), textPaint);
+
+ textPaint.setColor(Color.parseColor("#FF0000"));
+ canvas.drawText("Dodge the Enemy Birds", WIDTH / 4, HEIGHT * 3 / 4, textPaint);
+
+ textPaint.setColor(Color.parseColor("#FFFF00"));
+ textPaint.setFakeBoldText(true);
+ canvas.drawText("Collect the Bird Coins for Extra Lives", WIDTH / 4, HEIGHT * 3 / 4 + textPaint.getTextSize(), textPaint);
+ textPaint.setFakeBoldText(false);
+ }
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+
+ // Base Object Initializations
+ if (higherQualityEnabled) {
+ backgroundBitmap = BitmapFactory.decodeResource(resources, R.drawable.background_forest_without_clouds);
+ cloudsBitmap = BitmapFactory.decodeResource(resources, R.drawable.clouds);
+ }
+ else{
+ backgroundBitmap = BitmapFactory.decodeResource(resources, R.drawable.background_forest);
+ }
+ birdBitmap = BitmapFactory.decodeResource(resources, R.drawable.bird);
+ birdEnemyBitmap = BitmapFactory.decodeResource(resources, R.drawable.bird_enemy);
+ birdExtraLifeBitmap = BitmapFactory.decodeResource(resources, R.drawable.bird_extra_life);
+
+ // Game Object Initializations
+ background = new GameBackground(backgroundBitmap);
+ if (higherQualityEnabled) {
+ clouds = new GameBackground(cloudsBitmap);
+ }
+ bird = new Bird(birdBitmap, birdWidth, birdHeight, birdFrames);
+ birdEnemies = new ArrayList<>();
+ birdExtraLives = new ArrayList<>();
+
+ // Settings
+ setGameSpeed(birdGameSpeed);
+
+ super.surfaceCreated(holder);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (!bird.isPlaying()) {
+ bird.setPlaying(true);
+
+ int lifeMiddleOfScreen = HEIGHT/2 - birdExtraLifeHeight/2;
+ birdExtraLives.add(new ExtraLife(birdExtraLifeBitmap, birdExtraLifeWidth, birdExtraLifeHeight, WIDTH, lifeMiddleOfScreen, 1));
+ }
+ else{
+ bird.setFlapping(true);
+ }
+
+ return true;
+ }
+
+ if (event.getAction() == MotionEvent.ACTION_UP){
+ bird.setFlapping(false);
+
+ return true;
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ public void pauseGame() {
+ super.pauseGame(birdScoreKey, bird.getScore());
+ }
+
+ @Override
+ public void update() {
+ super.update();
+
+
+ if (bird.isPlaying()) {
+ background.update(gameSpeed);
+ if (higherQualityEnabled) {
+ clouds.update(cloudsSpeed);
+ }
+
+ int birdLevel = bird.getScore() / (birdEnemySpawnRate * birdMaxNumEnemyRows);
+
+ // Update extra lives before enemies :)
+ for (int i = 0; i < birdExtraLives.size(); i++) {
+ ExtraLife birdExtraLife = birdExtraLives.get(i);
+ boolean removeLife = false;
+
+ // Check if we've hit any extra lives
+ if (bird.getRect().intersect(birdExtraLife.getRect())){
+ bird.addLives(birdExtraLife.getNumLives());
+ removeLife = true;
+ }
+
+ if (removeLife || birdExtraLife.x + birdExtraLifeWidth < 0){
+ birdExtraLives.remove(birdExtraLife);
+ i--;
+ }
+ else{
+ birdExtraLife.update(birdGameSpeed);
+ }
+ }
+
+ /***** Add new lives *****/
+ // Only < so that lives don't appear until more than 1 bird can be on a row
+ if (birdExtraLives.size() < birdLevel){
+ int birdExtraLifeX = WIDTH + birdExtraLifeWidth;
+ int birdExtraLifeY = ((int)(Math.random() * HEIGHT));;
+ birdExtraLives.add(new ExtraLife(birdExtraLifeBitmap, birdExtraLifeWidth, birdExtraLifeHeight, birdExtraLifeX, birdExtraLifeY, 1));
+ }
+
+ // Update enemies
+ for (int i = 0; i < birdEnemies.size(); i++){
+ BirdEnemy birdEnemy = birdEnemies.get(i);
+ boolean birdEnemyRemove = false;
+
+ // Check if we've hit any enemies
+ if (bird.getRect().intersect(birdEnemy.getRect())){
+
+ bird.subtractLives(birdEnemy.getDamage());
+ birdEnemyRemove = true;
+
+ if (bird.getLives() < 0) {
+ /***** Stop the game, save the score, and reset the player and all objects *****/
+ bird.setPlaying(false);
+ saveScore(birdScoreKey, bird.getScore());
+
+ // Reset
+ bird = new Bird(birdBitmap, birdWidth, birdHeight, birdFrames);
+ birdEnemies = new ArrayList<>();
+ birdExtraLives = new ArrayList<>();
+ }
+ }
+
+ birdEnemy.update();
+
+ // Check if any enemies can be removed yet
+ if (birdEnemyRemove || birdEnemy.x + birdEnemy.width < 0){
+ birdEnemies.remove(birdEnemy);
+ i--;
+ }
+ }
+
+ // Check if we can add new enemies
+ if (birdEnemies.size() < bird.getScore() / 25 && !thread.markerFrame){
+ //int enemyX = WIDTH + (int)(WIDTH * Math.random());
+ int enemyX = WIDTH + birdWidth;
+ int enemyY = ((int)(Math.random() * HEIGHT));
+ // Add constant so that the bird is always slower than the background
+ // Prevents birds from looking frozen or like they're going backwards
+ int enemySpeed = gameSpeed + ((int)(Math.random() * birdEnemySpeedVariance) + birdEnemySpeedConstant);
+ int numBirdEnemiesInRow = 0;
+ for (BirdEnemy birdEnemy : birdEnemies){
+ Log.d(TAG, "GameView.update(): enemyY=" + enemyY + ", birdEnemy.y=" + birdEnemy.y);
+ if (birdEnemy.getRect().intersect(new Rect(birdEnemy.x, enemyY, birdEnemy.x + birdWidth, enemyY + birdHeight))){
+ numBirdEnemiesInRow++;
+ }
+ }
+ Log.d(TAG, "GameView.update(): birdLevel=" + birdLevel);
+ Log.d(TAG, "GameView.update(): numBirdEnemiesInRow=" + numBirdEnemiesInRow);
+
+ // Just for a little chaos, 1/100 chance for extra enemies
+ boolean addExtraEnemies = ((int) (Math.random() * 100)) == 69;
+
+ // <= so that the game can start
+ if (numBirdEnemiesInRow <= birdLevel || addExtraEnemies){
+ Log.d(TAG, "GameView.update(): Adding birdEnemy!");
+ birdEnemies.add(new BirdEnemy(birdEnemyBitmap, birdWidth, birdHeight, birdFrames, enemyX, enemyY, enemySpeed, 1));
+ }
+ }
+
+ bird.update();
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+
+ if (canvas != null) {
+ super.draw(canvas);
+
+ final float scaleX = (float) getWidth()/WIDTH;
+ final float scaleY = (float) getHeight()/HEIGHT;
+
+ Log.d(TAG, "GameView.draw(): Canvas not null, scaling! scaleX=" + scaleX + ", scaleY=" + scaleY);
+ Log.d(TAG, "GameView.draw(): Canvas not null, scaling! getScaleX()=" + getScaleX() + ", getScaleY()=" + getScaleY());
+
+ final int savedState = canvas.save();
+ canvas.scale(scaleX, scaleY);
+
+ // Draw all the game's objects
+ if (higherQualityEnabled) {
+ clouds.draw(canvas);
+ }
+
+ background.draw(canvas);
+
+ for (ExtraLife birdExtraLife : birdExtraLives) {
+ birdExtraLife.draw(canvas);
+ }
+
+ for (BirdEnemy birdEnemy : birdEnemies) {
+ birdEnemy.draw(canvas);
+ }
+
+ displayScore(canvas);
+
+ bird.draw(canvas);
+
+ // Prevent canvas from infinite scaling
+ canvas.restoreToCount(savedState);
+ }
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/Chicken.java b/app/src/main/java/com/hyperling/apps/games/Chicken.java
new file mode 100755
index 0000000..45e118f
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/Chicken.java
@@ -0,0 +1,103 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+
+/**
+ * Created by ling on 12/23/16.
+ */
+
+public class Chicken extends Player {
+
+ private boolean jumping, flapping;
+ private int lastY, lastDY;
+ private int leftFootX, rightFootX;
+
+ public Chicken(Bitmap b, int w, int h, int numFrames){
+ super(b, w, h, numFrames);
+ }
+
+ public boolean isJumping() {
+ return jumping;
+ }
+
+ public void setJumping(boolean jumping) {
+ this.jumping = jumping;
+ }
+
+ public boolean isFlapping() {
+ return flapping;
+ }
+
+ public void setFlapping(boolean flapping) {
+ this.flapping = flapping;
+ }
+
+ public void jump(){
+ // If we are already jumping then we should flap instead
+ if (jumping){
+ flap();
+ }
+ // Jumps are more powerful than flaps
+ else{
+ jumping = true;
+ dy -= 20;
+ }
+ }
+
+ private void flap(){
+ // Only allow a flap after we start falling
+ if (!flapping){
+ flapping = true;
+ dy -= 10;
+ }
+ }
+
+ public void update(int platformY) {
+ // If we are in the same Y position and are not flapping, we are on solid ground.
+ if (lastY == y && !flapping){
+ jumping = false;
+ }
+ // Allow flapping once the last flap wears off.
+ if (dy > 0){
+ flapping = false;
+ }
+
+ // Apply gravity
+ dy += 1;
+
+ // Do not allow super flapping
+ if (dy < -5){
+ dy = -5;
+ }
+ // Fall slower if we have jumped
+ else if (dy > 5 && jumping){
+ dy = 5;
+ }
+ // Otherwise cap gravity
+ else if (dy > 15 && !jumping){
+ dy = 15;
+ }
+
+ // Do not fall through platforms
+ if (y + dy > platformY - getHeight()){
+ setY(platformY - getHeight());
+ setDy(0);
+ }
+ else{
+ y += dy;
+ }
+
+ // Do not leave top of map
+ if (y < 0){
+ y = 0;
+ }
+
+ // Remember where we used to be
+ lastY = y;
+ lastDY = dy;
+
+ super.update();
+
+
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/ChickenGameView.java b/app/src/main/java/com/hyperling/apps/games/ChickenGameView.java
new file mode 100755
index 0000000..1791fe0
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/ChickenGameView.java
@@ -0,0 +1,320 @@
+package com.hyperling.apps.games;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+
+import java.util.ArrayList;
+
+/**
+ * Created by ling on 12/28/16.
+ */
+
+public class ChickenGameView extends GameView {
+
+ private Chicken chicken;
+ private Bitmap chickenBitmap;
+ private final int chickenWidth = 20;
+ private final int chickenHeight = 20;
+ private final int chickenFrames = 2;
+
+ private final int lavaHeight = 30;
+
+ private ArrayList platforms;
+ private Bitmap platformBitmap;
+ private final int platformWidth = 32;
+ private final int platformHeight = 400;
+ private final int platformDefaultY = HEIGHT - lavaHeight;
+
+ private Bitmap backgroundBitmap, lavaBitmap, cloudsBitmap;
+
+ private int chickenGameSpeed = -4;
+ private int cloudsSpeed = chickenGameSpeed / 2;
+ private int lavaSpeed = chickenGameSpeed - 1;
+
+ private ArrayList enemies;
+ private Bitmap enemyBitmap;
+ private final int enemyWidth = 32;
+ private final int enemyHeight = 32;
+ private final int enemyFrames = 1;
+ private final int enemySpeed = chickenGameSpeed * 2;
+
+ private String chickenScoreKey;
+
+ private int maxGapSize = 200;
+
+ public ChickenGameView(Context context) {
+ super(context);
+
+ // Keys
+ chickenScoreKey = resources.getString(R.string.chickenScoreKey);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ setGameSpeed(chickenGameSpeed);
+
+ // Create global Bitmaps
+ if (higherQualityEnabled) {
+ backgroundBitmap = BitmapFactory.decodeResource(resources, R.drawable.background_forest_without_clouds);
+ lavaBitmap = BitmapFactory.decodeResource(resources, R.drawable.lava);
+ cloudsBitmap = BitmapFactory.decodeResource(resources, R.drawable.clouds);
+ }
+ else{
+ backgroundBitmap = BitmapFactory.decodeResource(resources, R.drawable.background_forest_with_lava);
+ }
+ chickenBitmap = BitmapFactory.decodeResource(resources, R.drawable.chicken);
+ platformBitmap = BitmapFactory.decodeResource(resources, R.drawable.normal_platform);
+ enemyBitmap = BitmapFactory.decodeResource(resources, R.drawable.enemy);
+
+ // Initialize Background(s)
+ background = new GameBackground(backgroundBitmap);
+ if (higherQualityEnabled) {
+ clouds = new GameBackground(cloudsBitmap);
+ lava = new GameBackground(lavaBitmap);
+ }
+
+ resetPlatforms();
+
+ enemies = new ArrayList<>();
+
+ chicken = new Chicken(chickenBitmap, chickenWidth, chickenHeight, chickenFrames);
+
+ super.surfaceCreated(holder);
+ }
+
+ public void pauseGame() {
+ super.pauseGame(chickenScoreKey, chicken.getScore());
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (!chicken.isPlaying()) {
+ chicken.setPlaying(true);
+ }
+ else {
+ // Accelerate when moving higher in the air
+ /*
+ if (chicken.isJumping()) {
+ boost(2, 250);
+ }
+ // Boost harder on the a jump than a flap
+ else{
+ boost(2, 500);
+ }
+ */
+ chicken.jump();
+ }
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ private void resetPlatforms(){
+ platforms = new ArrayList<>();
+
+ // Create initial platform layer
+ int platformEndX = 0;
+ while (platformEndX < WIDTH){
+ Platform platform = new Platform(platformBitmap, platformWidth, platformHeight, platformEndX, platformDefaultY);
+ platformEndX = platform.x + platform.width;
+
+ platforms.add(platform);
+ }
+ }
+
+ private void displayScore(Canvas canvas){
+ int highScore = sharedPreferences.getInt(chickenScoreKey, 0);
+ textPaint.setColor(Color.parseColor("#330066"));
+ canvas.drawText("High: " + highScore, 0, textPaint.getTextSize(), textPaint);
+ canvas.drawText("Current: " + chicken.getScore(), 0, textPaint.getTextSize() * 2, textPaint);
+
+ if (fpsEnabled){
+ canvas.drawText("FPS: " + thread.averageFPS, 0, HEIGHT - 1, textPaint);
+ }
+
+ if (!chicken.isPlaying()) {
+ textPaint.setColor(Color.parseColor("#330066"));
+ canvas.drawText("You are the Purple and White Chicken", WIDTH / 4, HEIGHT / 4 - textPaint.getTextSize(), textPaint);
+
+ textPaint.setColor(Color.parseColor("#FF9900"));
+ canvas.drawText("Tap the Screen to Start", WIDTH / 4, HEIGHT / 4, textPaint);
+ canvas.drawText("Tap the Screen to Jump and Flap", WIDTH / 4, HEIGHT / 4 + textPaint.getTextSize(), textPaint);
+
+ textPaint.setColor(Color.parseColor("#FF0000"));
+ canvas.drawText("Don't fall into the lava!", WIDTH / 4, HEIGHT * 3 / 4, textPaint);
+ }
+ }
+
+ public void endGame(){
+ // Stop the game, save, then reset the game
+ chicken.setPlaying(false);
+ saveScore(chickenScoreKey, chicken.getScore());
+
+ resetPlatforms();
+ enemies = new ArrayList<>();
+ chicken = new Chicken(chickenBitmap, chickenWidth, chickenHeight, chickenFrames);
+ }
+
+ @Override
+ public void update() {
+ super.update();
+
+ if (chicken.isPlaying()) {
+ background.update(gameSpeed);
+ if (higherQualityEnabled) {
+ clouds.update(cloudsSpeed);
+ lava.update(lavaSpeed);
+ }
+
+ // Remove old platforms
+ for (int i = 0; i < platforms.size(); i++) {
+ Platform platform = platforms.get(i);
+
+ platform.update(gameSpeed);
+
+ if (platform.x + platform.width < 0) {
+ platforms.remove(platform);
+ i--;
+ }
+ }
+
+ // Add new platform(s) if the last platform is already being displayed
+ // Check where the last platform left off
+ boolean createPlatforms = true;
+ int lastPlatformEndX = 0;
+ for (Platform platform : platforms) {
+ if (platform.x > WIDTH) {
+ createPlatforms = false;
+ break;
+ } else {
+ lastPlatformEndX = platform.x + platform.width;
+ }
+ }
+
+ if (createPlatforms && !thread.markerFrame) {
+ // Get X
+ int newPlatformX;
+ // Chance of a gap
+ boolean createGap = ((int) (Math.random() * 100) > 69);
+ if (createGap) {
+ newPlatformX = lastPlatformEndX + (int) (Math.random() * maxGapSize);
+ } else {
+ newPlatformX = lastPlatformEndX;
+ }
+
+ // Get Y
+ int newPlatformY = platformDefaultY - ((int) (platformHeight * Math.random())) / (chickenHeight * 2);
+
+ // Add the platform
+ platforms.add(new Platform(platformBitmap, platformWidth, platformHeight, newPlatformX, newPlatformY));
+ }
+
+ // Check if we fell in a hole
+ if (chicken.y > HEIGHT) {
+ endGame();
+ }
+
+ // Refresh enemies array
+ boolean enemyOnTop = false;
+ for (int i = 0; i < enemies.size() && i >= 0; i++) {
+ Enemy enemy = enemies.get(i);
+
+ Log.d(TAG, "ChickenGameView.update(): enemy.getBottomY()=" + enemy.getBottomY());
+ // Check if we hit an enemy
+ if (chicken.getRect().intersect(enemy.getRect())) {
+ endGame();
+ break;
+ }
+ // Check if enemy is off the screen
+ else if (enemy.getEndX() <= 0) {
+ enemies.remove(enemy);
+ i--;
+ }
+ // Verify there is still an enemy on top
+ else {
+ if (enemy.y < chickenHeight) {
+ enemyOnTop = true;
+ }
+ }
+ }
+ Log.d(TAG, "ChickenGameView.update(): enemyOnTop=" + enemyOnTop);
+
+ // Always prioritize an enemy on the top, else create normal enemies above all the platforms
+ if (!thread.markerFrame) {
+ if (!enemyOnTop) {
+ int enemyY = (int) (chickenHeight * Math.random());
+ enemies.add(new Enemy(enemyBitmap, enemyWidth, enemyHeight, enemyFrames, WIDTH, enemyY, enemySpeed, 1));
+ }
+ else if ((chicken.getScore() / 25) > enemies.size()) {
+ int newEnemyHeight = HEIGHT;
+
+ // Make sure the enemy is always above the platforms
+ for (Platform platform : platforms) {
+ if (platform.y < newEnemyHeight) {
+ newEnemyHeight = platform.y;
+ }
+ }
+ newEnemyHeight -= enemyHeight * 2;
+ newEnemyHeight = (int) (newEnemyHeight * Math.random());
+ enemies.add(new Enemy(enemyBitmap, enemyWidth, enemyHeight, enemyFrames, WIDTH, newEnemyHeight, enemySpeed, 1));
+ }
+ }
+
+ for (Enemy enemy : enemies){
+ enemy.update();
+ }
+
+ chicken.update(findPlatformY(chicken, platforms));
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+
+ if (canvas != null) {
+ super.draw(canvas);
+ final float scaleX = (float) getWidth() / WIDTH;
+ final float scaleY = (float) getHeight() / HEIGHT;
+
+ Log.d(TAG, "GameView.draw(): Canvas not null, scaling! scaleX=" + scaleX + ", scaleY=" + scaleY);
+ Log.d(TAG, "GameView.draw(): Canvas not null, scaling! getScaleX()=" + getScaleX() + ", getScaleY()=" + getScaleY());
+
+ final int savedState = canvas.save();
+ canvas.scale(scaleX, scaleY);
+
+ // Draw all the game's objects
+ if (higherQualityEnabled) {
+ clouds.draw(canvas);
+ }
+
+ background.draw(canvas);
+
+ if (higherQualityEnabled) {
+ lava.draw(canvas);
+ }
+
+ for (Platform platform : platforms) {
+ platform.draw(canvas);
+ }
+
+ for (Enemy enemy : enemies){
+ enemy.draw(canvas);
+ }
+
+ displayScore(canvas);
+
+ chicken.draw(canvas);
+
+ // Prevent canvas from infinite scaling
+ canvas.restoreToCount(savedState);
+ }
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/Enemy.java b/app/src/main/java/com/hyperling/apps/games/Enemy.java
new file mode 100755
index 0000000..73f331c
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/Enemy.java
@@ -0,0 +1,72 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+/**
+ * Created by ling on 12/26/16.
+ */
+
+public class Enemy extends GameObject {
+
+ protected Bitmap image;
+
+ private Animation animation;
+
+ private int speed, damage;
+
+ public Enemy(Bitmap b, int w, int h, int numFrames, int x, int y, int speed, int damage){
+ image = b;
+ width = w;
+ height = h;
+ this.x = x;
+ this.y = y;
+ this.speed = speed;
+ this.damage = damage;
+
+ if (speed > -1){
+ speed = -1;
+ }
+ if (this.y >= GameView.HEIGHT - this.height){
+ this.y = GameView.HEIGHT - this.height;
+ }
+
+ // Create animation
+ Bitmap[] image = new Bitmap[numFrames];
+ animation = new Animation();
+
+ for (int i = 0; i < numFrames; i++){
+ image[i] = Bitmap.createBitmap(b, width * i, 0, width, height);
+ }
+
+ animation.setFrames(image);
+
+ //animation.setDelay(100);
+ int delay = 25 * Math.abs(speed);
+ if (delay > 125){
+ delay = 125;
+ }
+ else if (delay < 75){
+ delay = 75;
+ }
+ animation.setDelay(delay);
+ animation.setCurrentFrame((int)(Math.random() * numFrames));
+ }
+
+ public void update(){
+ animation.update();
+
+ // Move the object
+ x += speed;
+ super.updateEdgeLocations();
+ }
+
+ public void draw(Canvas canvas) {
+ // Draw the object
+ canvas.drawBitmap(animation.getImage(), getX(), getY(), null);
+ }
+
+ public int getDamage() {
+ return damage;
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/EnvironmentObject.java b/app/src/main/java/com/hyperling/apps/games/EnvironmentObject.java
new file mode 100755
index 0000000..96acca9
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/EnvironmentObject.java
@@ -0,0 +1,34 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+/**
+ * Created by ling on 12/23/16.
+ */
+
+public abstract class EnvironmentObject extends GameObject {
+
+ protected Bitmap image;
+
+ public EnvironmentObject(Bitmap i, int w, int h, int x, int y){
+ image = i;
+ width = w;
+ height = h;
+ this.x = x;
+ this.y = y;
+ }
+
+ public void update(int gameSpeed){
+ // Move the object
+ x += gameSpeed;
+
+ setEndX(getX() + getWidth());
+ setCenterX((getX() + getEndX()) / 2);
+ }
+
+ public void draw(Canvas canvas) {
+ // Draw the object
+ canvas.drawBitmap(image, x, y, null);
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/ExtraLife.java b/app/src/main/java/com/hyperling/apps/games/ExtraLife.java
new file mode 100755
index 0000000..f9c2178
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/ExtraLife.java
@@ -0,0 +1,25 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+
+/**
+ * Created by ling on 12/26/16.
+ */
+
+public class ExtraLife extends EnvironmentObject {
+
+ private int numLives;
+
+ public ExtraLife(Bitmap b, int w, int h, int x, int y, int numLives){
+ super(b, w, h, x, y);
+ this.numLives = numLives;
+
+ if (this.y >= GameView.HEIGHT - this.height){
+ this.y = GameView.HEIGHT - this.height;
+ }
+ }
+
+ public int getNumLives() {
+ return numLives;
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/GameActivity.java b/app/src/main/java/com/hyperling/apps/games/GameActivity.java
new file mode 100755
index 0000000..96edaa1
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/GameActivity.java
@@ -0,0 +1,56 @@
+package com.hyperling.apps.games;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Window;
+import android.view.WindowManager;
+
+/**
+ * Created by ling on 12/17/16.
+ */
+
+public class GameActivity extends AppCompatActivity {
+
+ BirdGameView birdGameView;
+ ChickenGameView chickenGameView;
+
+ private String gameType, birdGame, cowGame, pigGame, chickenGame, catGame;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ gameType = GameActivity.this.getIntent().getAction();
+
+ // Game types
+ birdGame = getString(R.string.btnGameBird);
+ cowGame = getString(R.string.btnGameCow);
+ pigGame = getString(R.string.btnGamePig);
+ chickenGame = getString(R.string.btnGameChicken);
+ catGame = getString(R.string.btnGameCat);
+
+ if (gameType.equals(birdGame)) {
+ birdGameView = new BirdGameView(GameActivity.this);
+ setContentView(birdGameView);
+ }
+ else if (gameType.equals(chickenGame)) {
+ chickenGameView = new ChickenGameView(GameActivity.this);
+ setContentView(chickenGameView);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+
+ if (gameType.equals(birdGame)) {
+ birdGameView.pauseGame();
+ }
+ else if (gameType.equals(chickenGame)) {
+ chickenGameView.pauseGame();
+ }
+ super.onPause();
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/GameBackground.java b/app/src/main/java/com/hyperling/apps/games/GameBackground.java
new file mode 100755
index 0000000..965692d
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/GameBackground.java
@@ -0,0 +1,44 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+/**
+ * Created by ling on 12/17/16.
+ */
+
+public class GameBackground {
+
+ private Bitmap image;
+ private int x, y;
+
+ public GameBackground(Bitmap i){
+ image = i;
+ }
+
+ public void update(int gameSpeed){
+ // Move the background
+ x += gameSpeed;
+
+ // If the image is completely off the screen, put it back to the middle.
+ if (x <= -GameView.WIDTH || x >= GameView.WIDTH){
+ x = 0;
+ }
+ }
+
+ public void draw(Canvas canvas){
+ // Draw the current image
+ canvas.drawBitmap(image, x, y, null);
+
+ // Place two more to the right since we are to the left
+ if (x < 0){
+ canvas.drawBitmap(image, x + GameView.WIDTH, y, null);
+ //canvas.drawBitmap(image, x + (2 * GameView.WIDTH), y, null);
+ }
+ // Place two more to the left since we are to the right
+ else if (x > 0){
+ canvas.drawBitmap(image, x - GameView.WIDTH, y, null);
+ //canvas.drawBitmap(image, x - (2 * GameView.WIDTH), y, null);
+ }
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/GameLoopThread.java b/app/src/main/java/com/hyperling/apps/games/GameLoopThread.java
new file mode 100755
index 0000000..0ff1e13
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/GameLoopThread.java
@@ -0,0 +1,101 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Canvas;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+/**
+ * Created by ling on 12/17/16.
+ */
+
+public class GameLoopThread extends Thread {
+ private final int FPS = 30;
+ protected double averageFPS;
+ private SurfaceHolder surfaceHolder;
+ private GameView gameView;
+ private boolean running;
+ public static Canvas canvas;
+
+ public boolean markerFrame;
+
+ private String TAG;
+
+ public GameLoopThread(SurfaceHolder sh, GameView gv){
+ super();
+ surfaceHolder = sh;
+ gameView = gv;
+
+ TAG = gameView.getResources().getString(R.string.TAG);
+
+ markerFrame = false;
+ }
+
+ @Override
+ public void run() {
+ super.run();
+
+ long timeStart, timeRun, timeWait, timeTotal, timeTarget;
+ timeTotal = 0;
+ timeTarget = 1000/FPS;
+
+ int frameCount = 0;
+
+ while(running){
+ timeStart = System.currentTimeMillis();
+ canvas = null;
+
+ try{
+ canvas = this.surfaceHolder.lockCanvas();
+ synchronized (surfaceHolder){
+ this.gameView.update();
+ this.gameView.draw(canvas);
+ }
+ } catch(Exception e){
+ e.printStackTrace();
+ } finally {
+ if (canvas != null) {
+ try {
+ surfaceHolder.unlockCanvasAndPost(canvas);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ timeRun = (System.currentTimeMillis() - timeStart);
+ timeWait = timeTarget - timeRun;
+
+ if (timeWait > 0) {
+ try {
+ this.sleep(timeWait);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ timeTotal += System.currentTimeMillis()-timeStart;
+ frameCount++;
+ if (frameCount >= FPS){
+ averageFPS = 1000/((timeTotal/frameCount));
+ frameCount = 0;
+ timeTotal = 0;
+
+ Log.i(TAG, "GameLoopThread.run(): averageFPS=" + averageFPS);
+ Log.d(TAG, "GameLoopThread.run(): canvas.getHeight()=" + canvas.getHeight() + ", canvas.getWidth()=" + canvas.getWidth());
+
+ markerFrame = true;
+ }
+ else{
+ markerFrame = false;
+ }
+ }
+ }
+
+ public void setRunning(boolean r){
+ running = r;
+
+ if (running) {
+ this.start();
+ }
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/GameObject.java b/app/src/main/java/com/hyperling/apps/games/GameObject.java
new file mode 100755
index 0000000..d39e4d9
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/GameObject.java
@@ -0,0 +1,116 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Rect;
+
+/**
+ * Created by ling on 12/22/16.
+ * https://youtu.be/kGqKNpk6VJw?list=PLWweaDaGRHjvQlpLV0yZDmRKVBdy6rSlg
+ */
+
+public abstract class GameObject {
+ protected int x;
+ protected int y;
+ protected int dx;
+ protected int dy;
+ protected int width;
+ protected int height;
+
+ protected int centerX;
+ protected int endX;
+
+ protected int centerY;
+ protected int bottomY;
+
+ public int getX() {
+ return x;
+ }
+
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public void setY(int y) {
+ this.y = y;
+ }
+
+ public int getDx() {
+ return dx;
+ }
+
+ public void setDx(int dx) {
+ this.dx = dx;
+ }
+
+ public int getDy() {
+ return dy;
+ }
+
+ public void setDy(int dy) {
+ this.dy = dy;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public int getCenterX() {
+ return centerX;
+ }
+
+ public void setCenterX(int centerX) {
+ this.centerX = centerX;
+ }
+
+ public int getEndX() {
+ return endX;
+ }
+
+ public void setEndX(int endX) {
+ this.endX = endX;
+ }
+
+ public int getCenterY() {
+ return centerY;
+ }
+
+ public void setCenterY(int centerY) {
+ this.centerY = centerY;
+ }
+
+ public int getBottomY() {
+ return bottomY;
+ }
+
+ public void setBottomY(int bottomY) {
+ this.bottomY = bottomY;
+ }
+
+ public void updateEdgeLocations(){
+ endX = x + width;
+ bottomY = y + height;
+
+ centerX = (x + endX) / 2;
+ centerY = (y + bottomY) / 2;
+ }
+
+ public Rect getRect(){
+ return new Rect(x, y, x + width, y + height);
+ }
+
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/GameView.java b/app/src/main/java/com/hyperling/apps/games/GameView.java
new file mode 100755
index 0000000..f5c4c92
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/GameView.java
@@ -0,0 +1,203 @@
+package com.hyperling.apps.games;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.text.TextPaint;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import java.util.ArrayList;
+
+/**
+ * Created by ling on 12/17/16.
+ * https://youtu.be/-XOMJYZmfkw?list=PLWweaDaGRHjvQlpLV0yZDmRKVBdy6rSlg
+ */
+
+public abstract class GameView extends SurfaceView implements SurfaceHolder.Callback{
+
+ public static final int WIDTH = 640;
+ public static final int HEIGHT = 400;
+
+ protected Resources resources;
+ protected final String sharedPreferencesKey, higherQualityKey, fpsKey;
+
+ protected static String TAG;
+
+ protected SharedPreferences sharedPreferences;
+ protected boolean higherQualityEnabled, fpsEnabled;
+
+ protected int gameSpeed = -5;
+ protected int boostQty;
+ protected long boostEnd;
+ protected boolean boosting;
+
+ protected GameLoopThread thread;
+ protected GameBackground background, clouds, lava;
+
+ protected TextPaint textPaint;
+
+ public GameView(Context context){
+ super(context);
+
+ // For filling strings and getting Bitmaps
+ resources = getResources();
+
+ // Keys
+ sharedPreferencesKey = resources.getString(R.string.sharedPreferencesKey);
+ higherQualityKey = resources.getString(R.string.higherQualityKey);
+ fpsKey = resources.getString(R.string.fpsKey);
+
+ // Initialize shared preferences
+ sharedPreferences = context.getSharedPreferences(sharedPreferencesKey, Context.MODE_PRIVATE);
+ higherQualityEnabled = sharedPreferences.getBoolean(higherQualityKey, false);
+ fpsEnabled = sharedPreferences.getBoolean(fpsKey, false);
+
+ // For output
+ TAG = resources.getString(R.string.TAG);
+
+ // Add the callback to SurfaceHolder (This allows control of events)
+ getHolder().addCallback(this);
+
+ // Initialize engine
+ thread = new GameLoopThread(getHolder(), this);
+ }
+
+ protected static String getTAG() {
+ return TAG;
+ }
+
+ protected void boost(int boostMultiplier, long boostDuration){
+ if (!boosting){
+ boosting = true;
+ boostQty = boostMultiplier;
+ boostEnd = System.currentTimeMillis() + boostDuration;
+
+ gameSpeed *= boostQty;
+ }
+ }
+
+ protected void setGameSpeed(int newSpeed){
+ gameSpeed = newSpeed;
+ }
+
+ protected int findPlatformY(Player player, ArrayList platforms){
+
+ // Get the tallest (smallest) platform.y where platform.y < player.y
+ int platformHighestY = HEIGHT*2;
+ for (Platform platform : platforms){
+
+ if (player.endX > platform.x &&
+ player.x < platform.endX &&
+ player.centerY <= platform.y &&
+ platform.y < platformHighestY){
+ Log.d(TAG, "GameView.findPlatformY():" +
+ " player.x=" + player.x +
+ ", player.endX=" + player.endX +
+ ", player.centerY=" + player.centerY +
+ ", platform.x=" + platform.x +
+ ", platform.endX=" + platform.endX +
+ ", platform.y=" + platform.y +
+ ", platformHighestY=" + platformHighestY);
+
+ platformHighestY = platform.y;
+ }
+ }
+
+ Log.d(TAG, "GameView.findPlatformY(): player.y=" + player.y + ", platformHighestY=" + platformHighestY);
+ return platformHighestY;
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ // Safe to start the thread
+ textPaint = new TextPaint();
+ textPaint.setTextSize(textPaint.getTextSize() * 2);
+
+ thread.setRunning(true);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Try to stop the thread
+ boolean retry = true;
+ int retryCount = 0;
+ while(retry && retryCount < 1000){
+ try{
+ thread.setRunning(false);
+ thread.join();
+ thread = null;
+ retry = false;
+ } catch(Exception e){
+ e.printStackTrace();
+ retryCount++;
+ }
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return super.onTouchEvent(event);
+ }
+
+ protected void pauseGame(String scoreKey, int score){
+ // Save and force a commit
+ saveScore(scoreKey, score);
+ sharedPreferences.edit().commit();
+
+ System.exit(1);
+ }
+
+ protected void saveScore(String scoreKey, int score){
+ // Check if score is better than high score
+ int oldScore = sharedPreferences.getInt(scoreKey, 0);
+ Log.d(TAG, "GameView.saveScore(): score=" + score + ", oldScore=" + oldScore);
+
+ if (score > oldScore) {
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.putInt(scoreKey, score);
+ editor.apply();
+ }
+
+ int highScore = sharedPreferences.getInt(scoreKey, 0);
+ Log.d(TAG, "GameView.saveScore(): Saved, new highScore=" + highScore);
+ }
+
+ protected void update(){
+ // Check if the boost is over and end it
+ if (boosting && System.currentTimeMillis() > boostEnd){
+ boosting = false;
+ gameSpeed /= boostQty;
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+
+ if (canvas != null) {
+ super.draw(canvas);
+ /*
+ final float scaleX = (float) getWidth()/WIDTH;
+ final float scaleY = (float) getHeight()/HEIGHT;
+ Log.d(TAG, "GameView.draw(): Canvas not null, scaling! scaleX=" + scaleX + ", scaleY=" + scaleY);
+ Log.d(TAG, "GameView.draw(): Canvas not null, scaling! getScaleX()=" + getScaleX() + ", getScaleY()=" + getScaleY());
+ final int savedState = canvas.save();
+ canvas.scale(scaleX, scaleY);
+
+ // Draw all the game's objects
+ background.draw(canvas);
+
+ // Prevent canvas from infinite scaling
+ canvas.restoreToCount(savedState);
+ */
+ }
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/HighScoreActivity.java b/app/src/main/java/com/hyperling/apps/games/HighScoreActivity.java
new file mode 100755
index 0000000..801e643
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/HighScoreActivity.java
@@ -0,0 +1,127 @@
+package com.hyperling.apps.games;
+
+import android.content.SharedPreferences;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.google.android.gms.ads.AdView;
+
+public class HighScoreActivity extends AppCompatActivity {
+
+ private String sharedPreferencesKey, adsKey, TAG, html;
+ private String[] allBirdScoreKeys, allChickenScoreKeys;
+
+ private SharedPreferences sharedPreferences;
+
+ LeaderboardHelper leaderboardHelper;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_high_score);
+
+ TAG = getString(R.string.TAG);
+
+ // Get keys
+ sharedPreferencesKey = getString(R.string.sharedPreferencesKey);
+ allBirdScoreKeys = getResources().getStringArray(R.array.allBirdScoreKeys);
+ allChickenScoreKeys = getResources().getStringArray(R.array.allChickenScoreKeys);
+ adsKey = getString(R.string.adsKey);
+
+ // Shared preferences
+ sharedPreferences = getSharedPreferences(sharedPreferencesKey, MODE_PRIVATE);
+
+ // Bird stuff
+ LinearLayout birdScoreLayout = (LinearLayout) findViewById(R.id.birdScoreLayout);
+ loadScoreLayout(birdScoreLayout, allBirdScoreKeys);
+
+ // Chicken stuff
+ LinearLayout chickenScoreLayout = (LinearLayout) findViewById(R.id.chickenScoreLayout);
+ loadScoreLayout(chickenScoreLayout, allChickenScoreKeys);
+ }
+
+ private void loadScoreLayout(LinearLayout layout, String[] scoreKeys){
+
+ for (String key : scoreKeys){
+ int score = sharedPreferences.getInt(key, 0);
+ if (score > 0){
+ String label = key;
+ // Add spaces to string before uppercase and numbers
+ label = label.replaceAll(" ", "");
+ //label = label.replaceAll("0", "");
+ for (int i = 1; i < label.length(); i++){
+ if (label.charAt(i) == label.toUpperCase().charAt(i) || isNumeric(label.charAt(i))){
+ label = label.substring(0, i) + " " + label.substring(i);
+ i++;
+ if (isNumeric(label.charAt(i))){
+ label = label.substring(0, i) + "- Version " + label.substring(i);
+ break;
+ }
+ }
+ }
+
+ label = label.concat(": ");
+ String scoreString = "" + score;
+
+ // Create UI Objects
+ LinearLayout linearLayout = new LinearLayout(this);
+ TextView tvLabel = new TextView(this);
+ TextView tvScore = new TextView(this);
+
+ // Set their text
+ tvLabel.setText(label);
+ tvScore.setText(scoreString);
+
+ // Finish the layout
+ linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ linearLayout.addView(tvLabel);
+ linearLayout.addView(tvScore);
+
+ // Add score to layout
+ layout.addView(linearLayout);
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ leaderboardHelper = new LeaderboardHelper();
+ displayAds();
+ super.onResume();
+
+ Runnable getHTML = new Runnable() {
+ @Override
+ public void run() {
+ html = leaderboardHelper.getTable("BirdScore009");
+ Log.d(TAG, "HighScoreActivity.onResume(): html=" + html);
+ }
+ };
+
+ Thread t = new Thread(getHTML);
+ t.start();
+
+ Log.d(TAG, "HighScoreActivity.onResume() html=" + html);
+ }
+
+ private void displayAds(){
+ AdsHelper.toggleAds(sharedPreferences, adsKey, getApplicationContext(), (AdView) findViewById(R.id.adViewHighScores));
+ }
+
+ private boolean isNumeric(char c){
+ try{
+ String s = "" + c;
+ int i = Integer.parseInt(s);
+ if (i >= 0) {
+ return true;
+ }
+ }
+ catch (Exception e){
+ //e.printStackTrace();
+ }
+
+ return false;
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/LeaderboardHelper.java b/app/src/main/java/com/hyperling/apps/games/LeaderboardHelper.java
new file mode 100755
index 0000000..ed65e27
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/LeaderboardHelper.java
@@ -0,0 +1,69 @@
+package com.hyperling.apps.games;
+
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+
+/**
+ * Created by ling on 1/3/17.
+ * Get Unique Device ID: http://stackoverflow.com/questions/16869482/how-to-get-unique-device-hardware-id-in-android#16869491
+ */
+
+public class LeaderboardHelper {
+
+ private final String baseURL = "https://hyperling.com:1337/hyper_games/";
+ private String getVariables = "";
+
+
+
+ public void addVariable(String key, String value){
+ if (getVariables.length() == 0) {
+ getVariables += "?";
+ }
+ else{
+ getVariables += "&";
+ }
+
+ getVariables += key + "=" + value;
+ }
+
+ public void clearVariables(){
+ getVariables = "";
+ }
+
+ public String getTable(String id){
+ String newURL = baseURL + "?leaderboard_id=" + id;
+ String html = "";
+
+ URL url;
+ InputStream is = null;
+ BufferedReader br;
+ String line;
+
+ try {
+ url = new URL(newURL);
+ is = url.openStream(); // throws an IOException
+ br = new BufferedReader(new InputStreamReader(is));
+
+ while ((line = br.readLine()) != null) {
+ html = html.concat(line);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (is != null) is.close();
+ } catch (IOException ioe) {
+ // nothing to see here
+ }
+ }
+
+ return html;
+
+ }
+
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/MainActivity.java b/app/src/main/java/com/hyperling/apps/games/MainActivity.java
new file mode 100755
index 0000000..3d8a97d
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/MainActivity.java
@@ -0,0 +1,171 @@
+package com.hyperling.apps.games;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import com.google.android.gms.ads.AdView;
+
+public class MainActivity extends AppCompatActivity {
+
+ private Button btnGameBird, btnGameCow, btnGamePig, btnGameChicken, btnGameCat, btnGameSquirrel, btnOptions, btnHighScores, btnHelp;
+
+ private String TAG, sharedPreferencesKey, adsKey, birdScoreKey, chickenScoreKey;
+
+ private SharedPreferences sharedPreferences;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_main);
+
+ TAG = getString(R.string.TAG);
+
+ // Keys
+ sharedPreferencesKey = getString(R.string.sharedPreferencesKey);
+ adsKey = getString(R.string.adsKey);
+ birdScoreKey = getString(R.string.birdScoreKey);
+ chickenScoreKey = getString(R.string.chickenScoreKey);
+
+ // Shared Preferences
+ sharedPreferences = getSharedPreferences(sharedPreferencesKey, MODE_PRIVATE);
+
+ // Initialize buttons
+ btnGameBird = (Button) findViewById(R.id.btnBirdGame);
+ btnGameCow = (Button) findViewById(R.id.btnGameCow);
+ btnGamePig = (Button) findViewById(R.id.btnGamePig);
+ btnGameChicken = (Button) findViewById(R.id.btnGameChicken);
+ btnGameCat = (Button) findViewById(R.id.btnGameCat);
+ btnGameSquirrel = (Button) findViewById(R.id.btnGameSquirrel);
+ btnOptions = (Button) findViewById(R.id.btnOptions);
+ btnHighScores = (Button) findViewById(R.id.btnHighScores);
+ btnHelp = (Button) findViewById(R.id.btnHelp);
+
+ // Set up game buttons
+ btnGameBird.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doGameButton((Button) v);
+ }
+ });
+ btnGameCow.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doGameButton((Button) v);
+ }
+ });
+ btnGamePig.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doGameButton((Button) v);
+ }
+ });
+ btnGameChicken.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doGameButton((Button) v);
+ }
+ });
+ btnGameCat.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doGameButton((Button) v);
+ }
+ });
+ btnGameSquirrel.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doGameButton((Button) v);
+ }
+ });
+
+ // Set up other buttons
+ btnOptions.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doOptionsButton();
+ }
+ });
+ btnHighScores.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doHighScoresButton();
+ }
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ displayAds();
+
+ // Append high scores to button text
+ int score;
+ String newText;
+
+ score = sharedPreferences.getInt(birdScoreKey, 0);
+ Log.d(TAG, "MainActivity.onResume(): birdScore=" + score);
+ if (score > 0) {
+ newText = getString(R.string.btnGameBird) + '\n' + getString(R.string.btnHighScoreSubHeader).concat(" ") + score;
+ Log.d(TAG, "MainActivity.onResume(): newText=" + newText);
+ btnGameBird.setText(newText);
+ }
+ else{
+ btnGameBird.setText(getString(R.string.btnGameBird));
+ }
+
+ score = sharedPreferences.getInt(chickenScoreKey, 0);
+ if (score > 0) {
+ newText = getString(R.string.btnGameChicken) + '\n' + getString(R.string.btnHighScoreSubHeader).concat(" ") + score;
+ Log.d(TAG, "MainActivity.onResume(): newText=" + newText);
+ btnGameChicken.setText(newText);
+ }
+ else{
+ btnGameChicken.setText(getString(R.string.btnGameChicken));
+ }
+
+ super.onResume();
+ }
+
+ // Start the game
+ private void doGameButton(Button b){
+ Log.i(TAG, "MainActivity.doCowGameButton()");
+
+ // Only get the game name, not the high score
+ String gameType, buttonText;
+ buttonText = b.getText().toString();
+ int newLine = buttonText.indexOf('\n');
+ if (newLine > 0) {
+ gameType = buttonText.substring(0, buttonText.indexOf('\n'));
+ }
+ else{
+ gameType = buttonText;
+ }
+
+ // Start the GameActivity
+ Intent game = new Intent(MainActivity.this, GameActivity.class);
+ game.setAction(gameType);
+ startActivity(game);
+ }
+
+ // Go to OptionsActivity
+ private void doOptionsButton(){
+ Log.i(TAG, "MainActivity.doOptionsButton()");
+
+ Intent options = new Intent(MainActivity.this, OptionsActivity.class);
+ startActivity(options);
+ }
+
+ private void displayAds(){
+ AdsHelper.toggleAds(sharedPreferences, adsKey, getApplicationContext(), (AdView) findViewById(R.id.adViewMain));
+ }
+
+ private void doHighScoresButton(){
+ Intent highScores = new Intent(MainActivity.this, HighScoreActivity.class);
+ startActivity(highScores);
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/OptionsActivity.java b/app/src/main/java/com/hyperling/apps/games/OptionsActivity.java
new file mode 100755
index 0000000..0268411
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/OptionsActivity.java
@@ -0,0 +1,187 @@
+package com.hyperling.apps.games;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.RelativeLayout;
+
+import com.google.android.gms.ads.AdRequest;
+import com.google.android.gms.ads.AdView;
+import com.google.android.gms.ads.MobileAds;
+
+/**
+ * Created by ling on 12/22/16.
+ */
+
+public class OptionsActivity extends AppCompatActivity {
+
+ private String sharedPreferencesKey, adsKey, higherQualityKey, fpsKey;
+
+ private SharedPreferences sharedPreferences;
+
+ private RelativeLayout layoutAds, layoutHigherQuality, layoutFPS;
+ private CheckBox cbAds, cbHigherQuality, cbFPS;
+ private boolean adsEnabled, higherQualityEnabled, fpsEnabled;
+
+ private Button btnDeletePreferences, btnConfirmDeletePreferences;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_options);
+
+ // Prevent the keyboard from popping up automatically
+ this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+
+ // Get SharedPreferences
+ sharedPreferencesKey = getString(R.string.sharedPreferencesKey);
+ sharedPreferences = getSharedPreferences(sharedPreferencesKey, MODE_PRIVATE);
+
+ // Set up Ads
+ adsKey = getString(R.string.adsKey);
+ layoutAds = (RelativeLayout) findViewById(R.id.optionAdsLayout);
+ cbAds = (CheckBox) findViewById(R.id.optionAdsCheckBox);
+ layoutAds.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ flipOption(adsKey);
+ }
+ });
+ cbAds.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ flipOption(adsKey);
+ }
+ });
+ setAds();
+
+ // Higher Quality Content
+ higherQualityKey = getString(R.string.higherQualityKey);
+ layoutHigherQuality = (RelativeLayout) findViewById(R.id.optionHigherQualityLayout);
+ cbHigherQuality = (CheckBox) findViewById(R.id.optionHigherQualityCheckBox);
+ layoutHigherQuality.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ flipOption(higherQualityKey);
+ }
+ });
+ cbHigherQuality.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ flipOption(higherQualityKey);
+ }
+ });
+ setHigherQuality();
+
+ // In-Game FPS
+ fpsKey = getString(R.string.fpsKey);
+ layoutFPS = (RelativeLayout) findViewById(R.id.optionFPSLayout);
+ cbFPS = (CheckBox) findViewById(R.id.fpsCheckBox);
+ layoutFPS.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ flipOption(fpsKey);
+ }
+ });
+ cbFPS.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ flipOption(fpsKey);
+ }
+ });
+ setFPS();
+
+ // Reset buttons
+ btnDeletePreferences = (Button) findViewById(R.id.btnDeletePreferences);
+ btnConfirmDeletePreferences = (Button) findViewById(R.id.btnConfirmDeletePreferences);
+
+ btnDeletePreferences.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showConfirmDeletePreferencesButton((Button) v);
+ }
+ });
+ btnConfirmDeletePreferences.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ deletePreferences();
+ }
+ });
+ }
+
+ private void flipOption(String key){
+ boolean option = sharedPreferences.getBoolean(key, false);
+
+ // Save the setting
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.putBoolean(key, !option);
+ editor.apply();
+
+ // Change the box
+ if (key.equals(adsKey)){
+ setAds();
+ }
+ else if (key.equals(higherQualityKey)){
+ setHigherQuality();
+ }
+ else if (key.equals(fpsKey)){
+ setFPS();
+ }
+ }
+
+ private void setAds(){
+ adsEnabled = sharedPreferences.getBoolean(adsKey, false);
+ cbAds.setChecked(adsEnabled);
+ toggleAds();
+ }
+
+ private void toggleAds(){
+ // Enable ads?
+ adsEnabled = sharedPreferences.getBoolean(adsKey, false);
+ AdView mAdView = (AdView) findViewById(R.id.adViewOptions);
+ if (adsEnabled) {
+ mAdView.setVisibility(View.VISIBLE);
+ MobileAds.initialize(getApplicationContext(), "ca-app-pub-3940256099942544~3347511713");
+ AdRequest adRequest = new AdRequest.Builder()
+ .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
+ .addTestDevice("C6A494DC6E7C9AC29102694D48487084") // Nexus 7
+ .addTestDevice("6545BCC9B60A394005546783687339B7") // Moto G
+ .build();
+ mAdView.loadAd(adRequest);
+ }
+ else{
+ mAdView.setVisibility(View.GONE);
+ }
+ }
+
+ public void setHigherQuality() {
+ higherQualityEnabled = sharedPreferences.getBoolean(higherQualityKey, false);
+ cbHigherQuality.setChecked(higherQualityEnabled);
+ }
+
+ public void setFPS() {
+ fpsEnabled = sharedPreferences.getBoolean(fpsKey, false);
+ cbFPS.setChecked(fpsEnabled);
+ }
+
+ private void showConfirmDeletePreferencesButton(Button callButton){
+ callButton.setEnabled(false);
+ btnConfirmDeletePreferences.setVisibility(View.VISIBLE);
+ btnConfirmDeletePreferences.setEnabled(true);
+ }
+
+ private void deletePreferences(){
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.clear();
+ editor.apply();
+
+ Intent resetScreen = new Intent(OptionsActivity.this, OptionsActivity.class);
+ startActivity(resetScreen);
+ finish();
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/Platform.java b/app/src/main/java/com/hyperling/apps/games/Platform.java
new file mode 100755
index 0000000..0b5686b
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/Platform.java
@@ -0,0 +1,19 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+
+/**
+ * Created by ling on 12/23/16.
+ */
+
+public class Platform extends EnvironmentObject {
+
+ public Platform(Bitmap b, int w, int h, int x, int y){
+ super(b, w, h, x, y);
+ }
+
+ @Override
+ public void update(int gameSpeed) {
+ super.update(gameSpeed);
+ }
+}
diff --git a/app/src/main/java/com/hyperling/apps/games/Player.java b/app/src/main/java/com/hyperling/apps/games/Player.java
new file mode 100755
index 0000000..c198458
--- /dev/null
+++ b/app/src/main/java/com/hyperling/apps/games/Player.java
@@ -0,0 +1,155 @@
+package com.hyperling.apps.games;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.util.Log;
+
+/**
+ * Created by ling on 12/22/16.
+ */
+
+public abstract class Player extends GameObject {
+
+ private String TAG;
+
+ //private Bitmap spritesheet;
+ private int score;
+
+ //private boolean up, down, left, right, sprint;
+ private boolean playing;
+ private Animation animation;
+ private long startTime;
+
+ public Player(Bitmap b, int w, int h, int numFrames){
+ TAG = GameView.getTAG();
+
+ setX(GameView.WIDTH/5);
+ setY(GameView.HEIGHT/2);
+ setDx(0);
+ setDy(0);
+
+ score = 0;
+
+ setWidth(w);
+ setHeight(h);
+
+ Bitmap[] image = new Bitmap[numFrames];
+ animation = new Animation();
+
+ for (int i = 0; i < numFrames; i++){
+ image[i] = Bitmap.createBitmap(b, width * i, 0, width, height);
+ }
+
+ animation.setFrames(image);
+ animation.setDelay(100);
+ startTime = System.currentTimeMillis();
+ }
+
+ public Player(){
+ super();
+ }
+
+ /*
+ public void setUp(boolean b){
+ up = b;
+ }
+
+ public void setDown(boolean b){
+ down = b;
+ }
+
+ public void setLeft(boolean b){
+ left = b;
+ }
+
+ public void setRight(boolean b){
+ right = b;
+ }
+
+ public void setSprint(boolean b){
+ sprint = b;
+ }
+ */
+
+ public boolean isPlaying(){
+ return playing;
+ }
+
+ public void setPlaying(boolean p){
+ playing = p;
+ }
+
+ public int getScore() {
+ return score;
+ }
+
+ public void update(){
+ animation.update();
+
+ long elapsed = (System.currentTimeMillis() - startTime);
+ if (elapsed > 100){
+ score++;
+ startTime = System.currentTimeMillis();
+ }
+
+ /* Specific animals should override this
+ if (left){
+ dx = Math.abs(dx) * -1;
+ }
+ else if (right){
+ dx = Math.abs(dx);
+ }
+
+ if (sprint){
+ dx *= 2;
+ }
+
+ if (up){
+ dy += -1;
+ }
+ else{
+ dy += 2;
+ }
+ if (dy < -5){
+ dy = -5;
+ }
+ if (dy > 10){
+ dy = 10;
+ }
+ */
+
+ /* Character specific as well
+ // Only move when going up or have not hit the ground yet.
+ if (y < GameView.HEIGHT - getHeight() || dy < 0) {
+ y += dy;
+ }
+ // Do not leave bottom of map
+ else{
+ y = GameView.HEIGHT - getHeight();
+ dy = 0;
+ }
+
+ // Do not leave top of map
+ if (y < 0){
+ y = 0;
+ }
+ */
+
+ setEndX(getX() + getWidth());
+ setCenterX((getX() + getEndX()) / 2);
+
+ setBottomY(getY() + getHeight());
+ setCenterY((getY() + getBottomY()) / 2);
+
+ Log.d(TAG, "Player.update():" +
+ " score=" + score +
+ ", x=" + x +
+ ", dx=" + dx +
+ ", y=" + y +
+ ", dy=" + dy);
+ }
+
+ public void draw(Canvas canvas){
+ canvas.drawBitmap(animation.getImage(), getX(), getY(), null);
+ }
+}
diff --git a/app/src/main/res/drawable-nodpi/background_forest.png b/app/src/main/res/drawable-nodpi/background_forest.png
new file mode 100755
index 0000000..550096a
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/background_forest.png differ
diff --git a/app/src/main/res/drawable-nodpi/background_forest_with_lava.png b/app/src/main/res/drawable-nodpi/background_forest_with_lava.png
new file mode 100755
index 0000000..9c7c8fe
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/background_forest_with_lava.png differ
diff --git a/app/src/main/res/drawable-nodpi/background_forest_without_clouds.png b/app/src/main/res/drawable-nodpi/background_forest_without_clouds.png
new file mode 100755
index 0000000..6a5c7a8
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/background_forest_without_clouds.png differ
diff --git a/app/src/main/res/drawable-nodpi/banner.png b/app/src/main/res/drawable-nodpi/banner.png
new file mode 100755
index 0000000..fb010d2
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/banner.png differ
diff --git a/app/src/main/res/drawable-nodpi/bird.png b/app/src/main/res/drawable-nodpi/bird.png
new file mode 100755
index 0000000..8a62f89
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/bird.png differ
diff --git a/app/src/main/res/drawable-nodpi/bird_enemy.png b/app/src/main/res/drawable-nodpi/bird_enemy.png
new file mode 100755
index 0000000..d9cd3e3
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/bird_enemy.png differ
diff --git a/app/src/main/res/drawable-nodpi/bird_extra_life.png b/app/src/main/res/drawable-nodpi/bird_extra_life.png
new file mode 100755
index 0000000..605d4f2
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/bird_extra_life.png differ
diff --git a/app/src/main/res/drawable-nodpi/chicken.png b/app/src/main/res/drawable-nodpi/chicken.png
new file mode 100755
index 0000000..5e846bc
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/chicken.png differ
diff --git a/app/src/main/res/drawable-nodpi/clouds.png b/app/src/main/res/drawable-nodpi/clouds.png
new file mode 100755
index 0000000..30444f6
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/clouds.png differ
diff --git a/app/src/main/res/drawable-nodpi/enemy.png b/app/src/main/res/drawable-nodpi/enemy.png
new file mode 100755
index 0000000..038e247
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/enemy.png differ
diff --git a/app/src/main/res/drawable-nodpi/icon.png b/app/src/main/res/drawable-nodpi/icon.png
new file mode 100755
index 0000000..965c520
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/icon.png differ
diff --git a/app/src/main/res/drawable-nodpi/lava.png b/app/src/main/res/drawable-nodpi/lava.png
new file mode 100755
index 0000000..a1e52a2
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/lava.png differ
diff --git a/app/src/main/res/drawable-nodpi/normal_platform.png b/app/src/main/res/drawable-nodpi/normal_platform.png
new file mode 100755
index 0000000..1186bbf
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/normal_platform.png differ
diff --git a/app/src/main/res/drawable-nodpi/test_player.png b/app/src/main/res/drawable-nodpi/test_player.png
new file mode 100755
index 0000000..6a55c5a
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/test_player.png differ
diff --git a/app/src/main/res/layout/activity_high_score.xml b/app/src/main/res/layout/activity_high_score.xml
new file mode 100755
index 0000000..49c3c95
--- /dev/null
+++ b/app/src/main/res/layout/activity_high_score.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100755
index 0000000..85eacc5
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_options.xml b/app/src/main/res/layout/activity_options.xml
new file mode 100755
index 0000000..9350c87
--- /dev/null
+++ b/app/src/main/res/layout/activity_options.xml
@@ -0,0 +1,292 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..cde69bc
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..c133a0c
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..bfa42f0
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..324e72c
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..aee44e1
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100755
index 0000000..63fc816
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100755
index 0000000..989c259
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ #330066
+ #333333
+ #FF9900
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100755
index 0000000..10acc92
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,9 @@
+
+
+ 16dp
+ 16dp
+
+ 30sp
+ 16dp
+ 1dp
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100755
index 0000000..292651f
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,92 @@
+
+ Hyper Games
+
+ apps.hyperling.com.platformer
+
+
+ Games
+ Betty the Bird (Beta)
+
+
+ Carly the Cow
+
+
+ Patty the Pig
+
+
+
+ Charlie the Chicken (Unfinished)
+
+
+ Kitty the Cat
+
+
+ Sammy the Squirrel
+
+ Options
+ High Scores
+ Help
+
+ High Score:
+
+
+
+ General
+ In Game
+ Online
+ Other
+
+ Allow Ads on Menu Screens?
+ Play Practice Round when Starting New Games?
+ Allow Higher Quality Images and Animations?
+ Display FPS?
+ Nickname for Online Leaderboards:
+ Allow Automatic High Score Uploads?
+
+ Type Name Here
+
+ Delete High Scores and Reset Preferences
+ Are you seriously sure that you seriously want to seriously delete everything? If so, press here!
+
+
+
+ HyperGames
+
+ Ads
+ Practice
+ HigherQuality
+ FPS
+ Nickname
+ Upload
+
+ BirdScore009
+ CowScore
+ PigScore
+ ChickenScore009
+ CatScore
+
+
+ Bird Game Scores
+
+ - BirdScore009
+ - BirdScore008(Beta)
+ - BirdScore007
+ - BirdScore005
+ - BirdScore004
+ - BirdScore003
+
+
+ Chicken Game Scores
+
+ - ChickenScore009
+ - ChickenScore008(Alpha)
+
+
+
+ ca-app-pub-9712416021907617/7877767281
+ ca-app-pub-9712416021907617/3307966880
+ ca-app-pub-9712416021907617/4784700088
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..a1cfc28
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/src/test/java/com/hyperling/apps/games/ExampleUnitTest.java b/app/src/test/java/com/hyperling/apps/games/ExampleUnitTest.java
new file mode 100755
index 0000000..e998d9d
--- /dev/null
+++ b/app/src/test/java/com/hyperling/apps/games/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.hyperling.apps.games;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100755
index 0000000..2cb309c
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,24 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ google()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.2.1'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/changelog.txt b/changelog.txt
new file mode 100755
index 0000000..3825c56
--- /dev/null
+++ b/changelog.txt
@@ -0,0 +1,39 @@
+-= v0.9.0 | 2017-02-05 =-
+Added Enemies to Chicken Game
+Began Work on High Score Uploads
+
+-= v0.8.1 | 2017-01-02 =-
+Fixed Clouds on Bird Game's Low Quality Background
+
+-= v008 | 2017-01-02 =-
+Moved Chicken Game to Alpha
+Added FPS and Fancy Graphics Settings
+
+-= v007 | 2016-12-28 =-
+Modified Bird Game Level Scaling
+Better Class Definitions and Code Cleanup (Increased Performance)
+Started Chicken Game
+
+-= v006 | 2016-12-26 =-
+Fixed High Score Being Erased
+
+-= v005 | 2016-12-26 =-
+Bird Game:
+ Added Lives
+ Added Dynamic Scaling
+ Added Instructions
+General:
+ Added Score Saving When Exiting Game
+ Fixed New Icon
+
+-= v004 | 2016-12-26 =-
+Finished Bird Game
+Added High Scores Menu
+Added Reset Button in Options
+Fixed Ads
+
+-= v003 | 2016-12-24 =-
+Fixed button not launching game correctly when high scores exist.
+
+-= v002 | 2016-12-24 =-
+Added High Score to Main Menu for Bird Game
diff --git a/gimp/BugByteBeach2.xcf b/gimp/BugByteBeach2.xcf
new file mode 100755
index 0000000..5c97958
Binary files /dev/null and b/gimp/BugByteBeach2.xcf differ
diff --git a/gimp/background.xcf b/gimp/background.xcf
new file mode 100755
index 0000000..925eb1a
Binary files /dev/null and b/gimp/background.xcf differ
diff --git a/gimp/background_forest.png b/gimp/background_forest.png
new file mode 100755
index 0000000..550096a
Binary files /dev/null and b/gimp/background_forest.png differ
diff --git a/gimp/background_forest.xcf b/gimp/background_forest.xcf
new file mode 100755
index 0000000..3afcf99
Binary files /dev/null and b/gimp/background_forest.xcf differ
diff --git a/gimp/background_forest_no_clouds.xcf b/gimp/background_forest_no_clouds.xcf
new file mode 100755
index 0000000..4b968f0
Binary files /dev/null and b/gimp/background_forest_no_clouds.xcf differ
diff --git a/gimp/background_forest_with_lava.png b/gimp/background_forest_with_lava.png
new file mode 100755
index 0000000..9c7c8fe
Binary files /dev/null and b/gimp/background_forest_with_lava.png differ
diff --git a/gimp/background_forest_without_clouds.png b/gimp/background_forest_without_clouds.png
new file mode 100755
index 0000000..6a5c7a8
Binary files /dev/null and b/gimp/background_forest_without_clouds.png differ
diff --git a/gimp/banner.png b/gimp/banner.png
new file mode 100755
index 0000000..fb010d2
Binary files /dev/null and b/gimp/banner.png differ
diff --git a/gimp/banner.xcf b/gimp/banner.xcf
new file mode 100755
index 0000000..b37c257
Binary files /dev/null and b/gimp/banner.xcf differ
diff --git a/gimp/bird.png b/gimp/bird.png
new file mode 100755
index 0000000..8a62f89
Binary files /dev/null and b/gimp/bird.png differ
diff --git a/gimp/bird.xcf b/gimp/bird.xcf
new file mode 100755
index 0000000..1cc591a
Binary files /dev/null and b/gimp/bird.xcf differ
diff --git a/gimp/bird_enemy.png b/gimp/bird_enemy.png
new file mode 100755
index 0000000..d9cd3e3
Binary files /dev/null and b/gimp/bird_enemy.png differ
diff --git a/gimp/bird_enemy.xcf b/gimp/bird_enemy.xcf
new file mode 100755
index 0000000..bacc97b
Binary files /dev/null and b/gimp/bird_enemy.xcf differ
diff --git a/gimp/bird_extra_life.png b/gimp/bird_extra_life.png
new file mode 100755
index 0000000..605d4f2
Binary files /dev/null and b/gimp/bird_extra_life.png differ
diff --git a/gimp/bird_extra_life.xcf b/gimp/bird_extra_life.xcf
new file mode 100755
index 0000000..c942061
Binary files /dev/null and b/gimp/bird_extra_life.xcf differ
diff --git a/gimp/chicken.png b/gimp/chicken.png
new file mode 100755
index 0000000..5e846bc
Binary files /dev/null and b/gimp/chicken.png differ
diff --git a/gimp/chicken.xcf b/gimp/chicken.xcf
new file mode 100755
index 0000000..4dcffec
Binary files /dev/null and b/gimp/chicken.xcf differ
diff --git a/gimp/clouds.png b/gimp/clouds.png
new file mode 100755
index 0000000..30444f6
Binary files /dev/null and b/gimp/clouds.png differ
diff --git a/gimp/enemy.png b/gimp/enemy.png
new file mode 100755
index 0000000..038e247
Binary files /dev/null and b/gimp/enemy.png differ
diff --git a/gimp/enemy.xcf b/gimp/enemy.xcf
new file mode 100755
index 0000000..4b5968c
Binary files /dev/null and b/gimp/enemy.xcf differ
diff --git a/gimp/extra_life.xcf b/gimp/extra_life.xcf
new file mode 100755
index 0000000..63ccebf
Binary files /dev/null and b/gimp/extra_life.xcf differ
diff --git a/gimp/icon.png b/gimp/icon.png
new file mode 100755
index 0000000..965c520
Binary files /dev/null and b/gimp/icon.png differ
diff --git a/gimp/icon.xcf b/gimp/icon.xcf
new file mode 100755
index 0000000..eda774c
Binary files /dev/null and b/gimp/icon.xcf differ
diff --git a/gimp/lava.png b/gimp/lava.png
new file mode 100755
index 0000000..a1e52a2
Binary files /dev/null and b/gimp/lava.png differ
diff --git a/gimp/lava.xcf b/gimp/lava.xcf
new file mode 100755
index 0000000..3abc816
Binary files /dev/null and b/gimp/lava.xcf differ
diff --git a/gimp/normal_platform.png b/gimp/normal_platform.png
new file mode 100755
index 0000000..1186bbf
Binary files /dev/null and b/gimp/normal_platform.png differ
diff --git a/gimp/normal_platform.xcf b/gimp/normal_platform.xcf
new file mode 100755
index 0000000..75f4908
Binary files /dev/null and b/gimp/normal_platform.xcf differ
diff --git a/gimp/player.xcf b/gimp/player.xcf
new file mode 100755
index 0000000..c423507
Binary files /dev/null and b/gimp/player.xcf differ
diff --git a/gimp/test_player.png b/gimp/test_player.png
new file mode 100755
index 0000000..6a55c5a
Binary files /dev/null and b/gimp/test_player.png differ
diff --git a/gradle.properties b/gradle.properties
new file mode 100755
index 0000000..aac7c9b
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..13372ae
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..c3de4ad
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Jan 10 06:49:36 CST 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100755
index 0000000..aec9973
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100755
index 0000000..e7b4def
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app'