Merge branch 'master' of https://github.com/LonamiWebs/Klooni1010
# Conflicts: # gradle/wrapper/gradle-wrapper.properties Also added Solarized Dark theme.
This commit is contained in:
commit
a4e9936a85
64 changed files with 1157 additions and 511 deletions
|
@ -21,6 +21,8 @@ public class Klooni extends Game {
|
|||
public static Theme theme;
|
||||
public Skin skin;
|
||||
|
||||
public ShareChallenge shareChallenge;
|
||||
|
||||
public static boolean onDesktop;
|
||||
|
||||
private final static float SCORE_TO_MONEY = 1f / 100f;
|
||||
|
@ -32,6 +34,12 @@ public class Klooni extends Game {
|
|||
|
||||
//region Creation
|
||||
|
||||
// TODO Possibly implement a 'ShareChallenge'
|
||||
// for other platforms instead passing null
|
||||
public Klooni(final ShareChallenge shareChallenge) {
|
||||
this.shareChallenge = shareChallenge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
onDesktop = Gdx.app.getType().equals(Application.ApplicationType.Desktop);
|
||||
|
|
87
core/src/io/github/lonamiwebs/klooni/ShareChallenge.java
Normal file
87
core/src/io/github/lonamiwebs/klooni/ShareChallenge.java
Normal file
|
@ -0,0 +1,87 @@
|
|||
package io.github.lonamiwebs.klooni;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.PixmapIO;
|
||||
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.glutils.FrameBuffer;
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label;
|
||||
import com.badlogic.gdx.utils.BufferUtils;
|
||||
import com.badlogic.gdx.utils.ScreenUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public abstract class ShareChallenge {
|
||||
|
||||
// Meant to return the file path to which the image will be saved
|
||||
// On some platforms it might be as simple as Gdx.files.local().file()
|
||||
abstract File getShareImageFilePath();
|
||||
|
||||
// Meant to share the saved screenshot at getShareImageFilePath()
|
||||
public abstract void shareScreenshot(final boolean saveResult);
|
||||
|
||||
// Saves the "Challenge me" shareable image to getShareImageFilePath()
|
||||
public boolean saveChallengeImage(final int score) {
|
||||
final File saveAt = getShareImageFilePath();
|
||||
if (!saveAt.getParentFile().isDirectory())
|
||||
if (!saveAt.mkdirs())
|
||||
return false;
|
||||
|
||||
final FileHandle output = new FileHandle(saveAt);
|
||||
|
||||
final Texture shareBase = new Texture(Gdx.files.internal("share.png"));
|
||||
final int width = shareBase.getWidth();
|
||||
final int height = shareBase.getHeight();
|
||||
|
||||
final FrameBuffer frameBuffer = new FrameBuffer(Pixmap.Format.RGB888, width, height, false);
|
||||
frameBuffer.begin();
|
||||
|
||||
// Render the base share texture
|
||||
final SpriteBatch batch = new SpriteBatch();
|
||||
final Matrix4 matrix = new Matrix4();
|
||||
matrix.setToOrtho2D(0, 0, width, height);
|
||||
batch.setProjectionMatrix(matrix);
|
||||
|
||||
Gdx.gl.glClearColor(Color.GOLD.r, Color.GOLD.g, Color.GOLD.b, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
batch.begin();
|
||||
batch.draw(shareBase, 0, 0);
|
||||
|
||||
// Render the achieved score
|
||||
final Label.LabelStyle style = new Label.LabelStyle();
|
||||
style.font = new BitmapFont(Gdx.files.internal("font/x1.0/geosans-light64.fnt"));
|
||||
Label label = new Label("just scored " + score + " on", style);
|
||||
label.setColor(Color.BLACK);
|
||||
label.setPosition(40, 500);
|
||||
label.draw(batch, 1);
|
||||
|
||||
label.setText("try to beat me if you can");
|
||||
label.setPosition(40, 40);
|
||||
label.draw(batch, 1);
|
||||
|
||||
batch.end();
|
||||
|
||||
// Get the framebuffer pixels and write them to a local file
|
||||
final byte[] pixels = ScreenUtils.getFrameBufferPixels(0, 0, width, height, true);
|
||||
|
||||
final Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
|
||||
|
||||
BufferUtils.copy(pixels, 0, pixmap.getPixels(), pixels.length);
|
||||
PixmapIO.writePNG(output, pixmap);
|
||||
|
||||
// Dispose everything
|
||||
pixmap.dispose();
|
||||
shareBase.dispose();
|
||||
batch.dispose();
|
||||
frameBuffer.end();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ public class SkinLoader {
|
|||
}
|
||||
|
||||
public static Texture loadPng(String name) {
|
||||
final String filename = "ui/x" + bestMultiplier + "/" + name + ".png";
|
||||
final String filename = "ui/x" + bestMultiplier + "/" + name;
|
||||
return new Texture(Gdx.files.internal(filename));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,19 +24,21 @@ public class Theme {
|
|||
private int price;
|
||||
|
||||
public Color background;
|
||||
public Color foreground;
|
||||
public Color emptyCell;
|
||||
|
||||
public Color currentScore;
|
||||
public Color highScore;
|
||||
public Color bonus;
|
||||
public Color bandColor;
|
||||
public Color textColor;
|
||||
|
||||
private Color[] cells;
|
||||
private Color[] buttons;
|
||||
|
||||
public static Skin skin;
|
||||
|
||||
public NinePatch cellPatch;
|
||||
public Texture cellTexture;
|
||||
|
||||
// Save the button styles so the changes here get reflected
|
||||
private ImageButton.ImageButtonStyle[] buttonStyles;
|
||||
|
@ -84,6 +86,20 @@ public class Theme {
|
|||
return new Theme().update(handle);
|
||||
}
|
||||
|
||||
// Used to determine the best foreground color (black or white) given a background color
|
||||
// Formula took from http://alienryderflex.com/hsp.html
|
||||
// Not used yet, but may be useful
|
||||
private final static double BRIGHTNESS_CUTOFF = 0.5;
|
||||
|
||||
public static boolean shouldUseWhite(Color color) {
|
||||
double brightness = Math.sqrt(
|
||||
color.r * color.r * .299 +
|
||||
color.g * color.g * .587 +
|
||||
color.b * color.b * .114);
|
||||
|
||||
return brightness < BRIGHTNESS_CUTOFF;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Theme updating
|
||||
|
@ -105,8 +121,9 @@ public class Theme {
|
|||
price = json.getInt("price");
|
||||
|
||||
JsonValue colors = json.get("colors");
|
||||
background = new Color( // Java won't allow unsigned integers, we need to use Long
|
||||
(int)Long.parseLong(colors.getString("background"), 16));
|
||||
// Java won't allow unsigned integers, we need to use Long
|
||||
background = new Color((int)Long.parseLong(colors.getString("background"), 16));
|
||||
foreground = new Color((int)Long.parseLong(colors.getString("foreground"), 16));
|
||||
|
||||
JsonValue buttonColors = colors.get("buttons");
|
||||
buttons = new Color[buttonColors.size];
|
||||
|
@ -125,6 +142,7 @@ public class Theme {
|
|||
highScore = new Color((int)Long.parseLong(colors.getString("high_score"), 16));
|
||||
bonus = new Color((int)Long.parseLong(colors.getString("bonus"), 16));
|
||||
bandColor = new Color((int)Long.parseLong(colors.getString("band"), 16));
|
||||
textColor = new Color((int)Long.parseLong(colors.getString("text"), 16));
|
||||
|
||||
emptyCell = new Color((int)Long.parseLong(colors.getString("empty_cell"), 16));
|
||||
|
||||
|
@ -135,8 +153,7 @@ public class Theme {
|
|||
}
|
||||
|
||||
String cellTextureFile = json.getString("cell_texture");
|
||||
cellPatch = new NinePatch(new Texture(
|
||||
Gdx.files.internal("ui/cells/"+cellTextureFile)), 4, 4, 4, 4);
|
||||
cellTexture = SkinLoader.loadPng("cells/"+cellTextureFile);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -65,9 +65,11 @@ public class Band extends Actor {
|
|||
|
||||
scoreLabel.setBounds(x + scoreBounds.x, y + scoreBounds.y, scoreBounds.width, scoreBounds.height);
|
||||
scoreLabel.setText(Integer.toString(scorer.getCurrentScore()));
|
||||
scoreLabel.setColor(Klooni.theme.textColor);
|
||||
scoreLabel.draw(batch, parentAlpha);
|
||||
|
||||
infoLabel.setBounds(x + infoBounds.x, y + infoBounds.y, infoBounds.width, infoBounds.height);
|
||||
infoLabel.setColor(Klooni.theme.textColor);
|
||||
infoLabel.draw(batch, parentAlpha);
|
||||
}
|
||||
|
||||
|
|
|
@ -182,6 +182,7 @@ public class MoneyBuyBand extends Table {
|
|||
}
|
||||
}
|
||||
setColor(Klooni.theme.bandColor);
|
||||
infoLabel.setColor(Klooni.theme.textColor);
|
||||
super.draw(batch, parentAlpha);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.github.lonamiwebs.klooni.actors;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
|
||||
|
@ -19,17 +20,21 @@ public class SoftButton extends ImageButton {
|
|||
|
||||
//region Constructor
|
||||
|
||||
public SoftButton(int styleIndex, String imageName) {
|
||||
public SoftButton(final int styleIndex, final String imageName) {
|
||||
super(Klooni.theme.getStyle(styleIndex));
|
||||
|
||||
this.styleIndex = styleIndex;
|
||||
image = Theme.skin.getDrawable(imageName);
|
||||
updateImage(imageName);
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Public methods
|
||||
|
||||
public void updateImage(final String imageName) {
|
||||
image = Theme.skin.getDrawable(imageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Batch batch, float parentAlpha) {
|
||||
// Always update the style to make sure we're using the right image.
|
||||
|
@ -40,6 +45,7 @@ public class SoftButton extends ImageButton {
|
|||
Klooni.theme.updateStyle(style, styleIndex);
|
||||
style.imageUp = image;
|
||||
|
||||
getImage().setColor(Klooni.theme.foreground);
|
||||
super.draw(batch, parentAlpha);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.github.lonamiwebs.klooni.actors;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
|
@ -28,11 +29,11 @@ public class ThemeCard extends Actor {
|
|||
|
||||
public float cellSize;
|
||||
|
||||
//endregion
|
||||
|
||||
//region Static members
|
||||
|
||||
private final static double BRIGHTNESS_CUTOFF = 0.5;
|
||||
private final static int colorsUsed[][] = {
|
||||
{0, 7, 7},
|
||||
{8, 7, 3},
|
||||
{8, 8, 3}
|
||||
};
|
||||
|
||||
//endregion
|
||||
|
||||
|
@ -48,7 +49,7 @@ public class ThemeCard extends Actor {
|
|||
priceLabel = new Label("", labelStyle);
|
||||
nameLabel = new Label(theme.getDisplay(), labelStyle);
|
||||
|
||||
Color labelColor = shouldUseWhite(theme.background) ? Color.WHITE : Color.BLACK;
|
||||
Color labelColor = Theme.shouldUseWhite(theme.background) ? Color.WHITE : Color.BLACK;
|
||||
priceLabel.setColor(labelColor);
|
||||
nameLabel.setColor(labelColor);
|
||||
|
||||
|
@ -69,21 +70,14 @@ public class ThemeCard extends Actor {
|
|||
|
||||
batch.setColor(theme.background);
|
||||
batch.draw(background, x, y, getWidth(), getHeight());
|
||||
// Do not draw on the borders (0,0 offset to add some padding), colors used:
|
||||
// 0 7 7
|
||||
// 8 7 3
|
||||
// 8 8 3
|
||||
Cell.draw(theme.getCellColor(0), batch, x + cellSize, y + cellSize, cellSize);
|
||||
Cell.draw(theme.getCellColor(7), batch, x + cellSize * 2, y + cellSize, cellSize);
|
||||
Cell.draw(theme.getCellColor(7), batch, x + cellSize * 3, y + cellSize, cellSize);
|
||||
|
||||
Cell.draw(theme.getCellColor(8), batch, x + cellSize, y + cellSize * 2, cellSize);
|
||||
Cell.draw(theme.getCellColor(7), batch, x + cellSize * 2, y + cellSize * 2, cellSize);
|
||||
Cell.draw(theme.getCellColor(8), batch, x + cellSize * 3, y + cellSize * 2, cellSize);
|
||||
|
||||
Cell.draw(theme.getCellColor(8), batch, x + cellSize, y + cellSize * 3, cellSize);
|
||||
Cell.draw(theme.getCellColor(8), batch, x + cellSize * 2, y + cellSize * 3, cellSize);
|
||||
Cell.draw(theme.getCellColor(3), batch, x + cellSize * 3, y + cellSize * 3, cellSize);
|
||||
// Avoid drawing on the borders by adding +1 cell padding
|
||||
for (int i = 0; i < colorsUsed.length; ++i) {
|
||||
for (int j = 0; j < colorsUsed[i].length; ++j) {
|
||||
Cell.draw(theme.cellTexture, theme.getCellColor(colorsUsed[i][j]), batch,
|
||||
x + cellSize * (j + 1), y + cellSize * (i + 1), cellSize);
|
||||
}
|
||||
}
|
||||
|
||||
nameLabel.setBounds(x + nameBounds.x, y + nameBounds.y, nameBounds.width, nameBounds.height);
|
||||
nameLabel.draw(batch, parentAlpha);
|
||||
|
@ -112,19 +106,4 @@ public class ThemeCard extends Actor {
|
|||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Private methods
|
||||
|
||||
// Used to determine the best foreground color (black or white) given a background color
|
||||
// Formula took from http://alienryderflex.com/hsp.html
|
||||
private static boolean shouldUseWhite(Color color) {
|
||||
double brightness = Math.sqrt(
|
||||
color.r * color.r * .299 +
|
||||
color.g * color.g * .587 +
|
||||
color.b * color.b * .114);
|
||||
|
||||
return brightness < BRIGHTNESS_CUTOFF;
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public abstract class BaseScorer implements BinSerializable {
|
|||
|
||||
// The board size is required when calculating the score
|
||||
BaseScorer(final Klooni game, GameLayout layout, int highScore) {
|
||||
cupTexture = SkinLoader.loadPng("cup");
|
||||
cupTexture = SkinLoader.loadPng("cup.png");
|
||||
cupColor = Klooni.theme.currentScore.cpy();
|
||||
cupArea = new Rectangle();
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.github.lonamiwebs.klooni.game;
|
|||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.math.Interpolation;
|
||||
|
@ -105,11 +106,18 @@ public class Cell implements BinSerializable {
|
|||
|
||||
//region Static methods
|
||||
|
||||
// TODO Use skin atlas
|
||||
public static void draw(Color color, Batch batch,
|
||||
float x, float y, float size) {
|
||||
// Default texture (don't call overloaded version to avoid overhead)
|
||||
public static void draw(final Color color, final Batch batch,
|
||||
final float x, final float y, final float size) {
|
||||
batch.setColor(color);
|
||||
Klooni.theme.cellPatch.draw(batch, x, y, size, size);
|
||||
batch.draw(Klooni.theme.cellTexture, x, y, size, size);
|
||||
}
|
||||
|
||||
// Custom texture
|
||||
public static void draw(final Texture texture, final Color color, final Batch batch,
|
||||
final float x, final float y, final float size) {
|
||||
batch.setColor(color);
|
||||
batch.draw(texture, x, y, size, size);
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
|
|
@ -125,6 +125,7 @@ class GameScreen implements Screen, InputProcessor, BinSerializable {
|
|||
if (!gameOverDone) {
|
||||
gameOverDone = true;
|
||||
|
||||
saveMoney();
|
||||
holder.enabled = false;
|
||||
pauseMenu.showGameOver(gameOverReason);
|
||||
if (Klooni.soundsEnabled())
|
||||
|
@ -150,13 +151,7 @@ class GameScreen implements Screen, InputProcessor, BinSerializable {
|
|||
|
||||
// Save the state, the user might leave the game in any of the following 2 methods
|
||||
private void showPauseMenu() {
|
||||
// Calculate new money since the previous saving
|
||||
int nowScore = scorer.getCurrentScore();
|
||||
int newMoneyScore = nowScore - savedMoneyScore;
|
||||
savedMoneyScore = nowScore;
|
||||
Klooni.addMoneyFromScore(newMoneyScore);
|
||||
|
||||
// Show the pause menu
|
||||
saveMoney();
|
||||
pauseMenu.show();
|
||||
save();
|
||||
}
|
||||
|
@ -275,6 +270,14 @@ class GameScreen implements Screen, InputProcessor, BinSerializable {
|
|||
|
||||
//region Saving and loading
|
||||
|
||||
private void saveMoney() {
|
||||
// Calculate new money since the previous saving
|
||||
int nowScore = scorer.getCurrentScore();
|
||||
int newMoneyScore = nowScore - savedMoneyScore;
|
||||
savedMoneyScore = nowScore;
|
||||
Klooni.addMoneyFromScore(newMoneyScore);
|
||||
}
|
||||
|
||||
private void save() {
|
||||
// Only save if the game is not over and the game mode is not the time mode. It
|
||||
// makes no sense to save the time game mode since it's supposed to be something quick.
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package io.github.lonamiwebs.klooni.screens;
|
||||
|
||||
import com.badlogic.gdx.Application;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.InputProcessor;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.math.Interpolation;
|
||||
|
@ -13,7 +16,10 @@ import com.badlogic.gdx.scenes.scene2d.actions.RunnableAction;
|
|||
import com.badlogic.gdx.scenes.scene2d.ui.Table;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.github.lonamiwebs.klooni.Klooni;
|
||||
import io.github.lonamiwebs.klooni.ShareChallenge;
|
||||
import io.github.lonamiwebs.klooni.actors.Band;
|
||||
import io.github.lonamiwebs.klooni.actors.SoftButton;
|
||||
import io.github.lonamiwebs.klooni.game.BaseScorer;
|
||||
|
@ -31,8 +37,10 @@ class PauseMenuStage extends Stage {
|
|||
|
||||
private final ShapeRenderer shapeRenderer;
|
||||
|
||||
private final Klooni game;
|
||||
private final Band band;
|
||||
private final BaseScorer scorer;
|
||||
private final SoftButton playButton;
|
||||
|
||||
//endregion
|
||||
|
||||
|
@ -40,6 +48,7 @@ class PauseMenuStage extends Stage {
|
|||
|
||||
// We need the score to save the maximum score if a new record was beaten
|
||||
PauseMenuStage(final GameLayout layout, final Klooni game, final BaseScorer scorer, final int gameMode) {
|
||||
this.game = game;
|
||||
this.scorer = scorer;
|
||||
|
||||
shapeRenderer = new ShapeRenderer(20); // 20 vertex seems to be enough for a rectangle
|
||||
|
@ -90,8 +99,7 @@ class PauseMenuStage extends Stage {
|
|||
});
|
||||
|
||||
// Continue playing OR share (if game over) button
|
||||
// TODO Enable both actions for this button? Or leave play?
|
||||
final SoftButton playButton = new SoftButton(2, "play_texture");
|
||||
playButton = new SoftButton(2, "play_texture");
|
||||
table.add(playButton).space(16);
|
||||
|
||||
playButton.addListener(new ChangeListener() {
|
||||
|
@ -143,6 +151,16 @@ class PauseMenuStage extends Stage {
|
|||
}
|
||||
|
||||
void showGameOver(final String gameOverReason) {
|
||||
if (game.shareChallenge != null) {
|
||||
playButton.updateImage("share_texture");
|
||||
playButton.addListener(new ChangeListener() {
|
||||
public void changed(ChangeEvent event, Actor actor) {
|
||||
game.shareChallenge.shareScreenshot(
|
||||
game.shareChallenge.saveChallengeImage(scorer.getCurrentScore()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
band.setMessage(gameOverReason);
|
||||
show();
|
||||
}
|
||||
|
@ -166,7 +184,9 @@ class PauseMenuStage extends Stage {
|
|||
// This is the only place where ShapeRenderer is OK because the batch hasn't started
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND);
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
shapeRenderer.setColor(1f, 1f, 1f, 0.3f);
|
||||
Color color = new Color(Klooni.theme.bandColor);
|
||||
color.a = 0.1f;
|
||||
shapeRenderer.setColor(color);
|
||||
shapeRenderer.rect(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
shapeRenderer.end();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue