diff --git a/frontend/android/assets/FruitSlicerBackground.png b/frontend/android/assets/FruitSlicerBackground.png new file mode 100644 index 0000000000000000000000000000000000000000..57d090558df46a372ee6a2dd86645af742ed2585 Binary files /dev/null and b/frontend/android/assets/FruitSlicerBackground.png differ diff --git a/frontend/android/assets/FruitSlicerPhotoEdit.png b/frontend/android/assets/FruitSlicerPhotoEdit.png new file mode 100644 index 0000000000000000000000000000000000000000..2ef01ec637069f306d44ce67d704f065ca4753eb Binary files /dev/null and b/frontend/android/assets/FruitSlicerPhotoEdit.png differ diff --git a/frontend/android/assets/sfx/FruitSlicerSquishSound.mp3 b/frontend/android/assets/sfx/FruitSlicerSquishSound.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..50b4128fd80e042af0e6f85265b7068ed3269d5b Binary files /dev/null and b/frontend/android/assets/sfx/FruitSlicerSquishSound.mp3 differ diff --git a/frontend/android/assets/sfx/FruitSlicerWhooshSound.mp3 b/frontend/android/assets/sfx/FruitSlicerWhooshSound.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d57dd2687c1d7614940b655e80a357add37c0107 Binary files /dev/null and b/frontend/android/assets/sfx/FruitSlicerWhooshSound.mp3 differ diff --git a/frontend/core/src/com/gameware/game/GameWare.java b/frontend/core/src/com/gameware/game/GameWare.java index 9e38ade34568dd86b780bc33144d519b74bcf638..6a17611e023944d9146e5b3cb0f2cb78ba2d0140 100644 --- a/frontend/core/src/com/gameware/game/GameWare.java +++ b/frontend/core/src/com/gameware/game/GameWare.java @@ -13,13 +13,13 @@ import com.gameware.game.models.LocalStorage; import com.gameware.game.states.BubbleWrapState; import com.gameware.game.models.Player; import com.gameware.game.states.ColorRushState; -import com.gameware.game.states.FruitCutState; +import com.gameware.game.states.FruitSlicerState; import com.gameware.game.states.GameStateManager; +import com.gameware.game.states.LoginState; import com.gameware.game.states.MenuState; +import com.gameware.game.states.PauseState; import com.gameware.game.states.PlayStateTemplate; -import com.gameware.game.states.LoginState; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -30,6 +30,7 @@ public class GameWare extends ApplicationAdapter { private SpriteBatch batch; private GameStateManager gsm; + private String fruitSlicerId = "5e90541aeba599f7b1bffceb"; private String colorRushId = "5e5d0efaa6e2bc5cb4920b7a"; private String bubbleWrapId = "5e5d0f1ea6e2bc5cb4920b7b"; @@ -68,8 +69,11 @@ public class GameWare extends ApplicationAdapter { } gsm = GameStateManager.getInstance(); + // Playable minigames + gameIdToPlayState.put(fruitSlicerId, new FruitSlicerState(gsm)); gameIdToPlayState.put(colorRushId, new ColorRushState(gsm)); gameIdToPlayState.put(bubbleWrapId, new BubbleWrapState(gsm)); + batch = new SpriteBatch(); music = Gdx.audio.newMusic(Gdx.files.internal(("bensound-goinghigher.mp3"))); diff --git a/frontend/core/src/com/gameware/game/sprites/FruitSprite.java b/frontend/core/src/com/gameware/game/sprites/FruitSprite.java index 0532672bb93f1b66aefa1601d264964b19c494ae..b64f969052d51d09f765a473be6b981124e06db2 100644 --- a/frontend/core/src/com/gameware/game/sprites/FruitSprite.java +++ b/frontend/core/src/com/gameware/game/sprites/FruitSprite.java @@ -15,12 +15,10 @@ public class FruitSprite extends Sprite { private Vector3 cutVelocity1; private Vector3 cutVelocity2; - private int cutRotationSpeed; + private int rotationSpeed; private int cutWidth; private int cutHeight; - private float totalLifetime; - private float currentLifetime; private float cuttingVelocity; private float gravity; @@ -30,7 +28,7 @@ public class FruitSprite extends Sprite { private com.badlogic.gdx.graphics.g2d.Sprite cutFruit1; private com.badlogic.gdx.graphics.g2d.Sprite cutFruit2; - public FruitSprite(int x, int y, int xVelocity, int yVelocity, int width, int height, Texture uncutFruitTexture){ + public FruitSprite(int x, int y, int width, int height, Vector3 velocity, Texture uncutFruitTexture){ this.position = new Vector3(x, y, 0); this.cutPosition1 = new Vector3(0,0,0); this.cutPosition2 = new Vector3(0,0,0); @@ -38,11 +36,11 @@ public class FruitSprite extends Sprite { this.cuttingVelocity = Gdx.graphics.getWidth()/2; this.gravity = Gdx.graphics.getWidth()/3; - this.uncutVelocity = new Vector3(xVelocity,yVelocity,0); + this.uncutVelocity = velocity; this.cutVelocity1 = new Vector3(-this.cuttingVelocity,0,0); this.cutVelocity2 = new Vector3(this.cuttingVelocity,0,0); - this.cutRotationSpeed = 180; + this.rotationSpeed = (int) ((Math.random() - 0.5) * 1000); this.width = width; this.height = height; this.cutWidth = width/2; @@ -50,9 +48,6 @@ public class FruitSprite extends Sprite { this.isCut = false; - this.totalLifetime = 5; - this.currentLifetime = 0; - TextureRegion fullTexRegion = new TextureRegion(uncutFruitTexture); int regionCutWidth = fullTexRegion.getRegionWidth()/2; @@ -92,9 +87,9 @@ public class FruitSprite extends Sprite { @Override public void update(float dt) { - this.currentLifetime += dt; - if(this.isCut){ + // Updates the position of the two fruit pieces based on its velocity + this.cutVelocity1.scl(dt); this.cutVelocity2.scl(dt); @@ -104,28 +99,33 @@ public class FruitSprite extends Sprite { this.cutVelocity1.scl(1/dt); this.cutVelocity2.scl(1/dt); + // Sets the position of the two fruit pieces this.cutFruit1.setPosition(this.cutPosition1.x, this.cutPosition1.y); this.cutFruit2.setPosition(this.cutPosition2.x, this.cutPosition2.y); + // Gravity affects the fruit pieces' velocity this.cutVelocity1.y -= this.gravity*2*dt; this.cutVelocity2.y -= this.gravity*2*dt; - this.cutFruit1.rotate(this.cutRotationSpeed/3*dt); - this.cutFruit2.rotate(-this.cutRotationSpeed/3*dt); + // Oppositely rotates the two pieces by a third of the original rotation speed + this.cutFruit1.rotate(this.rotationSpeed / 3*dt); + this.cutFruit2.rotate(-this.rotationSpeed / 3*dt); } else{ + // Updates the position of the full fruit based on its velocity this.uncutVelocity.scl(dt); this.position.add(this.uncutVelocity.x, this.uncutVelocity.y, 0); this.uncutVelocity.scl(1/dt); + // Gravity affects the fruit's velocity this.uncutVelocity.y -= this.gravity*dt; + // Sets the full fruit's size, position and rotation this.uncutFruit.setOriginCenter(); this.uncutFruit.setSize(this.width, this.height); this.uncutFruit.setPosition(this.position.x, this.position.y); - - this.uncutFruit.rotate(this.cutRotationSpeed*dt); + this.uncutFruit.rotate(this.rotationSpeed * dt); } } @@ -134,11 +134,13 @@ public class FruitSprite extends Sprite { } public boolean isDisposable() { - return this.currentLifetime >= this.totalLifetime; + // If the uncut fruit or the two cut fruit pieces have fallen this far in the y-direction, we know we can dispose it + return this.position.y < -this.height*3 || (this.cutPosition1.y < -this.height*3 && this.cutPosition2.y < -this.height*3); } public boolean isPressed(int x, int y) { - if(x > this.position.x && x < (this.position.x + this.width) && (Gdx.graphics.getHeight() - y) > this.position.y && (Gdx.graphics.getHeight() - y) < (this.position.y + this.height)) { + // If the user touched the fruit + if(!this.isCut && x > this.position.x && x < (this.position.x + this.width) && (Gdx.graphics.getHeight() - y) > this.position.y && (Gdx.graphics.getHeight() - y) < (this.position.y + this.height)) { this.isCut = true; // Updates the sprite positions @@ -151,6 +153,7 @@ public class FruitSprite extends Sprite { this.cutVelocity1.rotate(this.uncutFruit.getRotation(), 0, 0, 1); this.cutVelocity2.rotate(this.uncutFruit.getRotation(), 0, 0, 1); + // The two pieces keeps 25% of the original full fruits' velocity this.cutVelocity1.add((float)(this.uncutVelocity.x*0.25), (float)(this.uncutVelocity.y*0.25), 0); this.cutVelocity2.add((float)(this.uncutVelocity.x*0.25), (float)(this.uncutVelocity.y*0.25), 0); diff --git a/frontend/core/src/com/gameware/game/states/FruitCutState.java b/frontend/core/src/com/gameware/game/states/FruitCutState.java deleted file mode 100644 index cafdfef8aa6ed27ad53c248f5b38c6f310df9649..0000000000000000000000000000000000000000 --- a/frontend/core/src/com/gameware/game/states/FruitCutState.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.gameware.game.states; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.gameware.game.sprites.FruitSprite; - -import java.util.ArrayList; -import java.util.List; - -public class FruitCutState extends PlayStateTemplate { - private float currentDuration = 0f; - private float totalDuration = 30f; - private List<Texture> fruitTextures; - - private FruitSprite fruitSprite; - - public FruitCutState(GameStateManager gsm) { - super(gsm); - - this.fruitTextures = new ArrayList<>(); - - int textureNum = 0; - - while(textureNum < 20){ - textureNum++; - String filename = "FruitTexture" + String.valueOf(textureNum) + ".png"; - this.fruitTextures.add(new Texture(Gdx.files.internal(filename))); - } - - - Texture fruit = this.fruitTextures.get((int)(Math.random()*20)); - float fruitAspectRatio = Float.valueOf(fruit.getWidth()) / Float.valueOf(fruit.getHeight()); - int fruitWidth = 200; - int fruitHeight = (int) (fruitWidth/fruitAspectRatio); - fruitSprite = new FruitSprite(Gdx.graphics.getWidth()/2,Gdx.graphics.getWidth()/2,Gdx.graphics.getWidth()/2,Gdx.graphics.getWidth()/2,fruitWidth,fruitHeight,fruit); - } - - @Override - public void handleInput(){ - // ----- Din kode her ----- - - if(Gdx.input.justTouched()) { - int touchX = Gdx.input.getX(); - int touchY = Gdx.input.getY(); - - this.fruitSprite.isPressed(touchX, touchY); - } - // ----- Din kode her ----- - } - - @Override - public void update(float dt){ - super.update(dt); - this.handleInput(); - - // ----- Din kode her ----- - - this.currentDuration += dt; - - if(this.currentDuration > this.totalDuration){ - super.setGameFinished(); - } - - this.fruitSprite.update(dt); - - - // ----- Din kode her ----- - } - - @Override - public void render(SpriteBatch sb){ - // ----- Din kode her ----- - - this.fruitSprite.draw(sb); - - // ----- Din kode her ----- - - super.render(sb); - } - - @Override - public void dispose(){ - super.dispose(); - - // ----- Din kode her ----- - - // ----- Din kode her ----- - - } -} diff --git a/frontend/core/src/com/gameware/game/states/FruitSlicerState.java b/frontend/core/src/com/gameware/game/states/FruitSlicerState.java new file mode 100644 index 0000000000000000000000000000000000000000..fc607268e0fc1606a88e379bf60e156e29dbc637 --- /dev/null +++ b/frontend/core/src/com/gameware/game/states/FruitSlicerState.java @@ -0,0 +1,223 @@ +package com.gameware.game.states; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.audio.Sound; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.math.Vector3; +import com.gameware.game.GameWare; +import com.gameware.game.sprites.FruitSprite; + +import java.util.ArrayList; +import java.util.List; + +public class FruitSlicerState extends PlayStateTemplate { + private float currentDuration = 0f; + private float totalDuration = 60f; + private float startingEmitFrequency = 1f; + private float endingEmitFrequency = 0.2f; + private float timeSinceLastEmit = 0; + private int totalFruitsCut = 0; + private BitmapFont font; + private Texture background = new Texture(Gdx.files.internal("FruitSlicerBackground.png")); + private List<Texture> fruitTextures; + private List<FruitSprite> emittedFruits; + private Sound sliceWhooshSound; + private Sound sliceSquishSound; + + private FruitSprite fruitSprite; + + public FruitSlicerState(GameStateManager gsm) { + super(gsm); + super.setPauseButtonWhite(); + super.screenshot = new Texture(Gdx.files.internal("FruitSlicerPhotoEdit.png")); + + this.sliceWhooshSound = Gdx.audio.newSound(Gdx.files.internal("sfx/FruitSlicerWhooshSound.mp3")); + this.sliceSquishSound = Gdx.audio.newSound(Gdx.files.internal("sfx/FruitSlicerSquishSound.mp3")); + + this.fruitTextures = new ArrayList<>(); + this.emittedFruits = new ArrayList<>(); + + // Creates the bitmapfont + font = new BitmapFont(); + font.setColor(Color.WHITE); + font.getData().setScale(Gdx.graphics.getWidth()/ GameWare.WIDTH*2); + + for(int textureNum = 1; textureNum <= 20; textureNum++){ + String filename = "FruitTexture" + String.valueOf(textureNum) + ".png"; + this.fruitTextures.add(new Texture(Gdx.files.internal(filename))); + } + } + + @Override + public void handleInput(){ + if(Gdx.input.justTouched()) { + int touchX = Gdx.input.getX(); + int touchY = Gdx.input.getY(); + boolean didCut = false; + + for(FruitSprite fruit : this.emittedFruits){ + if(fruit.isPressed(touchX, touchY)){ + this.totalFruitsCut++; + didCut = true; + } + } + + if(didCut){ + this.sliceWhooshSound.play(); + this.sliceSquishSound.play(0.15f); + } + + super.setScore(this.totalFruitsCut); + } + } + + @Override + public void update(float dt){ + // Effectively updates the pause button and the loading text + super.update(dt); + + this.handleInput(); + + this.currentDuration += dt; + this.timeSinceLastEmit += dt; + + if(this.currentDuration > this.totalDuration){ + super.setGameFinished(); + } + + // Increases the emiting frequency towards the endingEmitFrequency as the game gets closer to the end + float currentFrequency = (float) (this.startingEmitFrequency - (this.startingEmitFrequency - this.endingEmitFrequency) * Math.min(this.currentDuration / (this.totalDuration * 0.6), 1)); + + // Emits a new fruit + if(this.timeSinceLastEmit > currentFrequency){ + this.emitNewFruit(); + this.timeSinceLastEmit = 0; + } + + for(FruitSprite fruit : this.emittedFruits){ + fruit.update(dt); + } + + // Removes the oldest fruit if it is disposable + if(this.emittedFruits.size() > 0 && this.emittedFruits.get(0).isDisposable()){ + this.emittedFruits.get(0).dispose(); + this.emittedFruits.remove(this.emittedFruits.get(0)); + } + } + + @Override + public void render(SpriteBatch sb){ + sb.begin(); + + // Background + sb.draw(this.background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + + sb.end(); + + // Fruits + for(FruitSprite fruit : this.emittedFruits){ + fruit.draw(sb); + } + + sb.begin(); + + // Time left + this.font.draw(sb, String.valueOf(Math.max(Math.round((this.totalDuration - this.currentDuration) * 100), 0.00) / 100.0), Gdx.graphics.getWidth()/40,Gdx.graphics.getHeight() - Gdx.graphics.getHeight()/40); + + sb.end(); + + //Effectively renders the pause button and the loading text + super.render(sb); + } + + @Override + public void dispose(){ + super.dispose(); + + this.background.dispose(); + this.font.dispose(); + this.sliceWhooshSound.dispose(); + this.sliceSquishSound.dispose(); + + for(Texture fruit : this.fruitTextures){ + fruit.dispose(); + } + } + + public void emitNewFruit() { + // Four different emit modes: from left, from right, from entire bottom, and from bottom center with different velocity angles + int emitMode = (int) (Math.random() * 4); + FruitSprite fruitSprite; + Texture fruitTexture = this.fruitTextures.get((int) (Math.random() * 20)); + Vector3 velocity = new Vector3(Gdx.graphics.getWidth() * 3 / 4, 0, 0); + + int x, y, emitAngle, rotationSpeed; + + float fruitAspectRatio = Float.valueOf(fruitTexture.getWidth()) / Float.valueOf(fruitTexture.getHeight()); + int fruitWidth = Gdx.graphics.getWidth() / 5; + int fruitHeight = (int) (fruitWidth / fruitAspectRatio); + + // Emit from left + if (emitMode == 0){ + x = -fruitWidth * 2; + y = (int) (Math.random() * Gdx.graphics.getHeight() / 3); + emitAngle = (int) (15 + Math.random() * 60); + + velocity.rotate(emitAngle, 0, 0, 1); + + fruitSprite = new FruitSprite(x, y, fruitWidth, fruitHeight, velocity, fruitTexture); + this.emittedFruits.add(fruitSprite); + } + + // Emit from right + else if(emitMode == 1) { + x = Gdx.graphics.getWidth() + fruitWidth * 2; + y = (int) (Math.random() * Gdx.graphics.getHeight() / 3); + emitAngle = (int) (180 - (15 + Math.random() * 60)); + + velocity.rotate(emitAngle, 0, 0, 1); + + fruitSprite = new FruitSprite(x, y, fruitWidth, fruitHeight, velocity, fruitTexture); + this.emittedFruits.add(fruitSprite); + } + + // Emit from entire bottom part of the screen + else if(emitMode == 2){ + int xWidth = Gdx.graphics.getWidth() + fruitWidth * 2; + x = (int)(xWidth*Math.random()); + y = -fruitHeight*2; + float emitAngleOffset = 30 - 30 * (Float.valueOf(x) / Float.valueOf(xWidth)); + emitAngle = (int) (105 - emitAngleOffset); + + velocity.x = (int) (velocity.x * (1.1 + 0.5 * (15 - Math.abs(90 - Float.valueOf(emitAngle)))/15)); + velocity.rotate(emitAngle, 0, 0, 1); + + fruitSprite = new FruitSprite(x - fruitWidth, y, fruitWidth, fruitHeight, velocity, fruitTexture); + this.emittedFruits.add(fruitSprite); + } + + // Emit from bottom center, with random emit angles + else if(emitMode == 3){ + x = Gdx.graphics.getWidth()/2 + fruitWidth/2; + y = - fruitHeight*2; + emitAngle = (int) (100 - Math.random()*20); + + velocity.x = (int) (velocity.x * 1.4); + velocity.rotate(emitAngle, 0, 0, 1); + + fruitSprite = new FruitSprite(x - fruitWidth, y, fruitWidth, fruitHeight, velocity, fruitTexture); + this.emittedFruits.add(fruitSprite); + } + } + + @Override + public void reset(){ + this.currentDuration = 0f; + this.timeSinceLastEmit = 0f; + this.totalFruitsCut = 0; + this.emittedFruits = new ArrayList<>(); + } +} diff --git a/frontend/core/src/com/gameware/game/states/MenuState.java b/frontend/core/src/com/gameware/game/states/MenuState.java index 8927d79ec9397ac217eeac51341db9eb0628f3a1..15b67e199820b0cc97fa10d0a554a376fef37410 100644 --- a/frontend/core/src/com/gameware/game/states/MenuState.java +++ b/frontend/core/src/com/gameware/game/states/MenuState.java @@ -18,8 +18,8 @@ public class MenuState extends State{ private final Label subHeadLabel = new Label("Welcome "+ GameWare.getInstance().getPlayer().getName()+"!", skin, "big"); // Button texts - private final String singlePlayerBtnText = "Single player"; - private final String multiPlayerBtnText = "Multi player"; + private final String singlePlayerBtnText = "Single-Player"; + private final String multiPlayerBtnText = "Multiplayer"; private final String highScoreBtnText = "High Scores"; private final String optionBtnText = "Options"; private final String logOutBtnText = "Log out";