/*
Point & Hit: A fast paced Android game
Copyright (C) 2015 Marien Raat
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jmstudios.pointandhit;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.jmstudios.pointandhit.UserPointer;
public class GameScreen implements Screen, InputProcessor {
public enum GameState { Running, Paused, GameOver, Calibrating };
GameState gameState;
SpriteBatch batch;
ShapeRenderer shapeRenderer;
Texture img;
UserPointer userPointer;
TargetManager targetManager;
BitmapFont scoreFont;
Vector2 screenSize;
OneShotGame game;
// GameState calibrating
float calibrationTime = 0.1f
, calibrationTimeLeft;
float roll, pitch, azimuth;
int frames;
// InputMultiplexer's
InputMultiplexer gameRunningMultiplexer
, gamePausedMultiplexer
, gameOverMultiplexer;
// Pause menu
float scale;
Stage pauseStage, gameOverStage, gameRunningStage;
Table pauseTable, gameOverTable
, gameOverButtonsTable;
BitmapFont textFont;
ImageButton resumeButton
, retryButton, menuButton
, pauseButton;
Texture transparentShader, heartTex;
Label gameOverText
, scoreTextLabel, scoreLabel
, highScoreTextLabel
, highScoreLabel;
ImageButton gameOverRetryButton
, gameOverMenuButton;
public GameScreen(final OneShotGame game) {
this.game = game;
this.scale = game.scale;
batch = new SpriteBatch();
shapeRenderer = new ShapeRenderer();
scoreFont = new BitmapFont(Gdx.files.internal("fonts/deja_vu_sans_large.fnt"));
scoreFont.setColor(Color.WHITE);
scoreFont.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
scoreFont.setScale(scale);
heartTex = new Texture(Gdx.files.internal("heart.png"));
screenSize = new Vector2(Gdx.graphics.getWidth()
, Gdx.graphics.getHeight());
setupMenus();
// Set up multiplexers
gameRunningMultiplexer = new InputMultiplexer();
gameRunningMultiplexer.addProcessor(gameRunningStage);
gameRunningMultiplexer.addProcessor(this);
gamePausedMultiplexer = new InputMultiplexer();
gamePausedMultiplexer.addProcessor(pauseStage);
gamePausedMultiplexer.addProcessor(this);
gameOverMultiplexer = new InputMultiplexer();
gameOverMultiplexer.addProcessor(gameOverStage);
gameOverMultiplexer.addProcessor(this);
}
public void setupMenus() {
Texture buttonsTex = game.buttons;
// Game running menu
gameRunningStage = new Stage();
TextureRegion pauseButtonTex = new TextureRegion(buttonsTex
, 512, 256, 128, 128);
pauseButton = new ImageButton(new TextureRegionDrawable
(pauseButtonTex));
pauseButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
pauseGame();
}
});
Table pauseButtonTable = new Table();
pauseButtonTable.setPosition(screenSize.x
- (pauseButton.getWidth() * scale)
, screenSize.y
- (pauseButton.getHeight() * scale));
pauseButtonTable.add(pauseButton)
.size(pauseButton.getWidth() * scale);
gameRunningStage.addActor(pauseButtonTable);
// Set up font
textFont = new BitmapFont(Gdx.files.internal("fonts/deja_vu_sans_medium.fnt"));
textFont.setColor(Color.WHITE);
textFont.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
textFont.setScale(scale);
// Set up transparent shader
Pixmap onePixTransparent = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
onePixTransparent.setColor(new Color(0,0,0,0.6f));
onePixTransparent.fill();
transparentShader = new Texture(onePixTransparent);
// Set up buttons
TextureRegion resumeTex = new TextureRegion(buttonsTex
, 0, 0, 256, 256)
, retryTex = new TextureRegion(buttonsTex
, 256, 0, 256, 256)
, menuTex = new TextureRegion(buttonsTex
, 512, 0, 256, 256);
TextureRegionDrawable resumeDrawable = new
TextureRegionDrawable(resumeTex)
, retryDrawable = new TextureRegionDrawable
(retryTex)
, menuDrawable = new TextureRegionDrawable
(menuTex);
resumeButton = new ImageButton(resumeDrawable);
retryButton = new ImageButton(retryDrawable);
menuButton = new ImageButton(menuDrawable);
// Click listeners
resumeButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
gameState = GameState.Running;
giveFocus();
}
});
retryButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
retry();
}
});
menuButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
goToMainMenu();
}
});
// Set up pauseTable
float padding = 20 * scale;
this.pauseStage = new Stage();
Table wholePauseTable = new Table();
wholePauseTable.setFillParent(true);
pauseTable = new Table();
pauseTable.defaults().pad(padding).padTop(padding * 3)
.padBottom(padding * 3); // Different screen sizes
pauseTable.add(resumeButton)
.size(resumeButton.getWidth() * scale);
pauseTable.add(retryButton)
.size(retryButton.getWidth() * scale);
pauseTable.add(menuButton)
.size(menuButton.getWidth() * scale);
wholePauseTable.add(pauseTable).fill(10, 1f);
pauseStage.addActor(wholePauseTable);
// Set up game over menu
gameOverStage = new Stage();
gameOverTable = new Table();
gameOverStage.addActor(gameOverTable);
// Labels
Label.LabelStyle scoreLabelStyle = new Label.LabelStyle(textFont
, Color.WHITE);
Label.LabelStyle gameOverLabelStyle = new Label.LabelStyle
(scoreFont, Color.WHITE);
gameOverText = new Label("Game over", gameOverLabelStyle);
scoreTextLabel = new Label("Score: ", scoreLabelStyle);
highScoreTextLabel = new Label("Highscore: ", scoreLabelStyle);
scoreLabel = new Label("null", gameOverLabelStyle);
highScoreLabel = new Label("null", scoreLabelStyle);
// Buttons
gameOverRetryButton = new ImageButton(retryDrawable);
gameOverMenuButton = new ImageButton(menuDrawable);
gameOverRetryButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
retry();
}
});
gameOverMenuButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
goToMainMenu();
}
});
gameOverButtonsTable = new Table();
gameOverButtonsTable.defaults().pad(padding * 2);
gameOverButtonsTable.add(gameOverRetryButton).left()
.size(gameOverRetryButton.getWidth() * scale);
gameOverButtonsTable.add(gameOverMenuButton).right()
.size(gameOverMenuButton.getHeight() * scale);
gameOverTable.setFillParent(true);
gameOverTable.setDebug(false);
gameOverTable.defaults().pad(padding * 2);
gameOverTable.add(gameOverText).colspan(3).center();
gameOverTable.row();
gameOverTable.add(scoreLabel).colspan(3).center()
.pad(8 * padding);
gameOverTable.row();
gameOverTable.add(highScoreLabel).colspan(3).center()
.padTop(0).padBottom(padding * 4);
gameOverTable.row();
gameOverTable.add(gameOverButtonsTable).center()
.fill(10f, 1f).padTop(padding * 8);
}
protected void goToMainMenu() {
game.setScreen(game.mainMenu);
}
protected void retry() {
gameState = GameState.Running;
show();
}
public void giveFocus() {
Gdx.input.setInputProcessor(gameRunningMultiplexer);
}
@Override
public void show() {
giveFocus();
Gdx.input.setCatchBackKey(true);
userPointer = new UserPointer((int) (screenSize.x / 40)
, screenSize, game.preferences);
targetManager = new TargetManager(2.1f
, (int) (screenSize.x * 0.255f)
, screenSize, userPointer, this);
// Calibration
roll = Gdx.input.getRoll();
pitch = Gdx.input.getPitch();
azimuth = Gdx.input.getAzimuth();
frames = 1;
calibrationTimeLeft = calibrationTime;
gameState = GameState.Calibrating;
}
public void updateGame() {
userPointer.update();
targetManager.update();
}
@Override
public void render(float delta) {
if (gameState == GameState.Calibrating) {
updateCalibrations();
renderUIOnly();
}
else if (gameState == GameState.Running) {
updateGame();
renderGame();
}
else if (gameState == GameState.Paused) {
renderGame();
renderPauseMenu();
}
else if (gameState == GameState.GameOver) {
renderGameOverMenu();
}
}
public void renderGame() {
Color backgroundColor = targetManager.currentTheme
.background;
Gdx.gl.glClearColor(backgroundColor.r
, backgroundColor.g
, backgroundColor.b, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Shape drawing
shapeRenderer.begin(ShapeType.Filled);
targetManager.draw(shapeRenderer);
userPointer.draw(shapeRenderer,
targetManager.currentTheme);
shapeRenderer.end();
// Sprite drawing
batch.begin();
targetManager.draw(batch);
CharSequence scoreString = ""
+ targetManager.getScore();
scoreFont.draw(batch, scoreString
, screenSize.x / 20, screenSize.y
- screenSize.x / 20);
drawHearts(targetManager.lifes, batch);
batch.end();
gameRunningStage.act();
gameRunningStage.draw();
}
public void renderUIOnly() {
Color backgroundColor = targetManager.currentTheme
.background;
Gdx.gl.glClearColor(backgroundColor.r
, backgroundColor.g
, backgroundColor.b, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
CharSequence scoreString = ""
+ targetManager.getScore();
scoreFont.draw(batch, scoreString
, screenSize.x / 20, screenSize.y
- screenSize.x / 20);
drawHearts(targetManager.lifes, batch);
batch.end();
gameRunningStage.act();
gameRunningStage.draw();
}
public void renderPauseMenu() {
batch.begin();
batch.draw(transparentShader, 0, 0
, screenSize.x, screenSize.y);
batch.end();
pauseStage.act();
pauseStage.draw();
}
public void renderGameOverMenu() {
// Clear screen
Color backgroundColor = targetManager.currentTheme
.background;
Gdx.gl.glClearColor(backgroundColor.r
, backgroundColor.g
, backgroundColor.b, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
gameOverStage.act();
gameOverStage.draw();
}
public void gameOver() {
gameState = GameState.GameOver;
Pixmap gameOverButtonsBackground = new Pixmap
(1, 1, Format.RGBA8888);
Color gameOverButtonsBackgroundColor = targetManager
.currentTheme.target.cpy();
gameOverButtonsBackgroundColor.a = 0.8f;
gameOverButtonsBackground.setColor
(gameOverButtonsBackgroundColor);
gameOverButtonsBackground.fill();
TextureRegionDrawable gameOverButtonsBackgroundDrawable
= new TextureRegionDrawable(new TextureRegion
(new Texture(gameOverButtonsBackground)));
gameOverButtonsTable.setBackground
(gameOverButtonsBackgroundDrawable);
scoreLabel.setText("" + targetManager.score);
scoreLabel.setColor(targetManager.currentTheme
.target.cpy().mul(1.2f));
int highscore = game.getHighScore();
if (targetManager.score > highscore) {
game.setHighScore(targetManager.score);
highscore = targetManager.score;
}
highScoreLabel.setText("Best: " + highscore); // Get HighScore
highScoreLabel.setColor(targetManager.currentTheme
.bullet);
Gdx.input.setInputProcessor(gameOverMultiplexer);
}
public void pauseGame() {
gameState = GameState.Paused;
Gdx.input.setInputProcessor(gamePausedMultiplexer);
Pixmap pauseMenuBackground = new Pixmap(1,1, Format.RGBA8888);
Color pauseMenuColor = targetManager.currentTheme.target.cpy();
pauseMenuColor.a = 0.8f;
pauseMenuBackground.setColor(pauseMenuColor);
pauseMenuBackground.fill();
pauseTable.setBackground(new TextureRegionDrawable
(new TextureRegion(new Texture(pauseMenuBackground))));
}
public void drawHearts(int hearts
, SpriteBatch batch) {
float padding = (screenSize.x / 20) * scale
, heartSize = 64 * scale
, xMid = screenSize.x / 2
, y = screenSize.y
- heartSize - padding;
switch (hearts) {
case 0:
break;
case 1:
float x = screenSize.x / 2
- heartSize / 2;
batch.draw(heartTex, x, y
, heartSize, heartSize);
break;
case 2:
float x2_1 = xMid - (padding / 2)
- heartSize
, x2_2 = xMid + (padding / 2);
batch.draw(heartTex, x2_1, y
, heartSize, heartSize);
batch.draw(heartTex, x2_2, y
, heartSize, heartSize);
break;
default:
float x3_1 = xMid - (1.5f * heartSize)
- padding
, x3_2 = screenSize.x / 2
- heartSize / 2
, x3_3 = xMid + (heartSize / 2)
+ padding;
batch.draw(heartTex, x3_1, y
, heartSize, heartSize);
batch.draw(heartTex, x3_2, y
, heartSize, heartSize);
batch.draw(heartTex, x3_3, y
, heartSize, heartSize);
break;
}
}
public void updateCalibrations() {
roll += Gdx.input.getRoll();
pitch += Gdx.input.getPitch();
azimuth += Gdx.input.getAzimuth();
frames++;
calibrationTimeLeft -= Gdx.graphics.getDeltaTime();
if (calibrationTimeLeft < 0) {
userPointer.calibrate(roll / frames
, pitch / frames
, azimuth / frames);
gameState = GameState.Running;
}
}
@Override
public void dispose() {
// TODO: Make dispose function
}
@Override
public void resize(int width, int height) {
// TODO Auto-generated method stub
}
@Override
public void pause() {
if (gameState == GameState.Running) {
pauseGame();
}
}
@Override
public void resume() {
}
@Override
public void hide() {
Gdx.input.setCatchBackKey(false);
}
@Override
public boolean keyDown(int keycode) {
if (keycode == Keys.BACK) {
if (gameState == GameState.Running) {
pauseGame();
}
else if (gameState == GameState.Paused) {
gameState = GameState.Running;
giveFocus();
}
else if (gameState == GameState.GameOver) {
goToMainMenu();
}
}
return false;
}
@Override
public boolean keyUp(int keycode) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean keyTyped(char character) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if (gameState == GameState.Running) {
targetManager.screenTouchDown();
}
else if (gameState == GameState.Paused) {
if (screenY < pauseTable.getY()
|| screenY > pauseTable.getY()
+ pauseTable.getHeight()) {
gameState = GameState.Running;
giveFocus();
}
}
return true;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean mouseMoved(int screenX, int screenY) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean scrolled(int amount) {
// TODO Auto-generated method stub
return false;
}
}