summaryrefslogtreecommitdiff
path: root/core/src/kz
diff options
context:
space:
mode:
authorilotterytea <iltsu@alright.party>2024-06-01 00:51:20 +0500
committerilotterytea <iltsu@alright.party>2024-06-01 00:51:20 +0500
commite49f8b310d6032c99406baf04b5ec3eba0fd111f (patch)
tree5120a1fbbf923da5ee8bc8561ed1545855aa5547 /core/src/kz
parent10e9df6190ddc3f9c8dd7c86482449bec4651e0c (diff)
upd: moved the whole project under kz.ilotterytea.maxon name
Diffstat (limited to 'core/src/kz')
-rw-r--r--core/src/kz/ilotterytea/maxon/MaxonConstants.java37
-rw-r--r--core/src/kz/ilotterytea/maxon/MaxonGame.java61
-rw-r--r--core/src/kz/ilotterytea/maxon/anim/SpriteUtils.java31
-rw-r--r--core/src/kz/ilotterytea/maxon/audio/Playlist.kt34
-rw-r--r--core/src/kz/ilotterytea/maxon/inputprocessors/CrossProcessor.java67
-rw-r--r--core/src/kz/ilotterytea/maxon/player/DecalPlayer.java75
-rw-r--r--core/src/kz/ilotterytea/maxon/player/MaxonItem.java23
-rw-r--r--core/src/kz/ilotterytea/maxon/player/MaxonItemEnum.java7
-rw-r--r--core/src/kz/ilotterytea/maxon/player/MaxonItemRegister.java39
-rw-r--r--core/src/kz/ilotterytea/maxon/player/MaxonPlayer.java26
-rw-r--r--core/src/kz/ilotterytea/maxon/player/MaxonSavegame.java40
-rw-r--r--core/src/kz/ilotterytea/maxon/player/utils/PetUtils.kt42
-rw-r--r--core/src/kz/ilotterytea/maxon/screens/GameScreen.java603
-rw-r--r--core/src/kz/ilotterytea/maxon/screens/MenuScreen.java380
-rw-r--r--core/src/kz/ilotterytea/maxon/screens/SplashScreen.java102
-rw-r--r--core/src/kz/ilotterytea/maxon/screens/game/shop/ShopMode.java6
-rw-r--r--core/src/kz/ilotterytea/maxon/screens/game/shop/ShopMultiplier.java17
-rw-r--r--core/src/kz/ilotterytea/maxon/screens/game/shop/ShopUI.java249
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/AnimatedImage.java60
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/AnimatedImageButton.java24
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/DebugInfo.kt56
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/InventoryAnimatedItem.java26
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/LeafParticle.java30
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/MovingChessBackground.java150
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/NinepatchButton.java28
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/OptionsTable.java180
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/PurchaseItem.java85
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/SavegameWidget.java202
-rw-r--r--core/src/kz/ilotterytea/maxon/ui/SupaIconButton.java24
-rw-r--r--core/src/kz/ilotterytea/maxon/utils/AssetLoading.java191
-rw-r--r--core/src/kz/ilotterytea/maxon/utils/GameUpdater.java127
-rw-r--r--core/src/kz/ilotterytea/maxon/utils/I18N.java53
-rw-r--r--core/src/kz/ilotterytea/maxon/utils/OsUtils.java109
-rw-r--r--core/src/kz/ilotterytea/maxon/utils/ScreenshotFactory.java51
-rw-r--r--core/src/kz/ilotterytea/maxon/utils/colors/HexToARGB.java14
-rw-r--r--core/src/kz/ilotterytea/maxon/utils/formatters/NumberFormatter.java36
-rw-r--r--core/src/kz/ilotterytea/maxon/utils/math/Math.java17
-rw-r--r--core/src/kz/ilotterytea/maxon/utils/serialization/GameDataSystem.java120
38 files changed, 3422 insertions, 0 deletions
diff --git a/core/src/kz/ilotterytea/maxon/MaxonConstants.java b/core/src/kz/ilotterytea/maxon/MaxonConstants.java
new file mode 100644
index 0000000..08df03c
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/MaxonConstants.java
@@ -0,0 +1,37 @@
+package kz.ilotterytea.maxon;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.files.FileHandle;
+import kz.ilotterytea.maxon.utils.OsUtils;
+
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+
+public class MaxonConstants {
+ public static final String GAME_NAME = "Maxon Petting Simulator";
+ public static final String GAME_VERSION = "Alpha 1.2";
+ public static final String GAME_GHTAG = "alpha-1.2";
+ public static final String GAME_PUBLISHER = "iLotterytea";
+ public static final String[][] GAME_DEVELOPERS = {
+ {"ilotterytea", "https://ilotterytea.kz"},
+ {"greddyss", "https://twitch.tv/greddyss"},
+ {"enotegg", "https://twitch.tv/enotegg"},
+ {"namesake", "https://twitter.com/nameisnamesake"},
+ {"saopin", "https://twitch.tv/saopin_"},
+ {"gvardovskiy", "https://twitch.tv/gvardovskiy"}
+ };
+
+ public static final String GAME_MAIN_FOLDER = OsUtils.getUserDataDirectory(".Maxoning");
+ public static final String GAME_SCREENSHOT_FOLDER = GAME_MAIN_FOLDER + "/screenshots";
+ public static final String GAME_SAVEGAME_FOLDER = GAME_MAIN_FOLDER + "/savegames";
+
+ public static final FileHandle FILE_EN_US = Gdx.files.internal("i18n/en_us.json");
+ public static final FileHandle FILE_RU_RU = Gdx.files.internal("i18n/ru_ru.json");
+
+ public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###,###.##");
+ public static final DecimalFormat DECIMAL_FORMAT2 = new DecimalFormat("###,###");
+ @SuppressWarnings("SimpleDateFormat")
+ public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd/M/yyyy hh:mm:ss");
+ public static final long startTime = System.currentTimeMillis();
+
+}
diff --git a/core/src/kz/ilotterytea/maxon/MaxonGame.java b/core/src/kz/ilotterytea/maxon/MaxonGame.java
new file mode 100644
index 0000000..b1e40d6
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/MaxonGame.java
@@ -0,0 +1,61 @@
+package kz.ilotterytea.maxon;
+
+import com.badlogic.gdx.Game;
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.Preferences;
+import com.badlogic.gdx.assets.AssetManager;
+import com.badlogic.gdx.graphics.g2d.SpriteBatch;
+import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
+import kz.ilotterytea.maxon.screens.SplashScreen;
+import kz.ilotterytea.maxon.utils.I18N;
+
+public class MaxonGame extends Game {
+ public SpriteBatch batch;
+ public ShapeRenderer shapeRenderer;
+ public AssetManager assetManager;
+ public Preferences prefs;
+ public I18N locale;
+
+ private static MaxonGame instance;
+
+ public static MaxonGame getInstance() {
+ if (instance == null) {
+ instance = new MaxonGame();
+ }
+ return instance;
+ }
+
+ @Override
+ public void create () {
+ batch = new SpriteBatch();
+ shapeRenderer = new ShapeRenderer();
+ prefs = Gdx.app.getPreferences("Maxoning");
+ locale = new I18N(Gdx.files.internal("i18n/" + prefs.getString("lang", "en_us") + ".json"));
+
+ prefs.putInteger("width", Gdx.graphics.getWidth());
+ prefs.putInteger("height", Gdx.graphics.getHeight());
+ prefs.flush();
+
+ Gdx.graphics.setVSync(prefs.getBoolean("vsync", true));
+ if (prefs.getBoolean("fullscreen", false)) { Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode()); }
+
+ assetManager = new AssetManager();
+
+ this.setScreen(new SplashScreen(this));
+ }
+
+ @Override
+ public void render () {
+ super.render();
+ }
+
+ @Override
+ public void dispose () {
+ batch.dispose();
+ for (String name : assetManager.getAssetNames()) {
+ assetManager.unload(name);
+ }
+ assetManager.dispose();
+ instance.dispose();
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/anim/SpriteUtils.java b/core/src/kz/ilotterytea/maxon/anim/SpriteUtils.java
new file mode 100644
index 0000000..3bc5712
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/anim/SpriteUtils.java
@@ -0,0 +1,31 @@
+package kz.ilotterytea.maxon.anim;
+
+import com.badlogic.gdx.graphics.Texture;
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+
+import java.util.Arrays;
+
+public class SpriteUtils {
+ public static TextureRegion[] splitToTextureRegions(
+ Texture texture,
+ int tileWidth,
+ int tileHeight,
+ int columns,
+ int rows
+ ) {
+ TextureRegion[][] tmp = TextureRegion.split(texture, tileWidth, tileHeight);
+ TextureRegion[] frames = new TextureRegion[(texture.getWidth() / columns) + (texture.getHeight() / rows)];
+
+ int index = 0;
+
+ for (TextureRegion[] regArray : tmp) {
+ for (TextureRegion reg : regArray) {
+ if (reg != null) {
+ frames[index++] = reg;
+ }
+ }
+ }
+
+ return frames;
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/audio/Playlist.kt b/core/src/kz/ilotterytea/maxon/audio/Playlist.kt
new file mode 100644
index 0000000..c1c56cf
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/audio/Playlist.kt
@@ -0,0 +1,34 @@
+package kz.ilotterytea.maxon.audio
+
+import com.badlogic.gdx.audio.Music
+import kz.ilotterytea.maxon.utils.math.Math
+
+/**
+ * Playlist.
+ */
+class Playlist(vararg musics: Music) {
+ private val playlist: Array<out Music> = musics
+ var playingNow: Music = playlist[0]
+ private var index = 0;
+
+ var shuffleMode = false
+
+ /**
+ * Play next music.
+ */
+ fun next() {
+ if (playingNow.isPlaying) playingNow.stop()
+
+ if (shuffleMode) {
+ index = Math.getRandomNumber(0, playlist.size - 1)
+ playingNow = playlist[index]
+ playingNow.play()
+ } else {
+ index++
+ if (index > playlist.size - 1) index = 0
+
+ playingNow = playlist[index]
+ playingNow.play()
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/inputprocessors/CrossProcessor.java b/core/src/kz/ilotterytea/maxon/inputprocessors/CrossProcessor.java
new file mode 100644
index 0000000..88d1078
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/inputprocessors/CrossProcessor.java
@@ -0,0 +1,67 @@
+package kz.ilotterytea.maxon.inputprocessors;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.Input;
+import com.badlogic.gdx.InputProcessor;
+import kz.ilotterytea.maxon.utils.ScreenshotFactory;
+
+public class CrossProcessor implements InputProcessor {
+ @Override
+ public boolean keyDown(int keycode) {
+ // Take a screenshot:
+ if (Gdx.input.isKeyPressed(Input.Keys.F2)) {
+ ScreenshotFactory.takeScreenshot();
+ return true;
+ }
+ // Toggle the fullscreen:
+ if (Gdx.input.isKeyPressed(Input.Keys.F11)) {
+ if (Gdx.graphics.isFullscreen()) {
+ Gdx.graphics.setWindowedMode(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
+ } else {
+ Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean keyUp(int keycode) {
+ return false;
+ }
+
+ @Override
+ public boolean keyTyped(char character) {
+ return false;
+ }
+
+ @Override
+ public boolean touchDown(int screenX, int screenY, int pointer, int button) {
+ return false;
+ }
+
+ @Override
+ public boolean touchUp(int screenX, int screenY, int pointer, int button) {
+ return false;
+ }
+
+ @Override
+ public boolean touchCancelled(int i, int i1, int i2, int i3) {
+ return false;
+ }
+
+ @Override
+ public boolean touchDragged(int screenX, int screenY, int pointer) {
+ return false;
+ }
+
+ @Override
+ public boolean mouseMoved(int screenX, int screenY) {
+ return false;
+ }
+
+ @Override
+ public boolean scrolled(float amountX, float amountY) {
+ return false;
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/player/DecalPlayer.java b/core/src/kz/ilotterytea/maxon/player/DecalPlayer.java
new file mode 100644
index 0000000..ca6d65e
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/player/DecalPlayer.java
@@ -0,0 +1,75 @@
+package kz.ilotterytea.maxon.player;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.graphics.Camera;
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.graphics.g3d.decals.Decal;
+import com.badlogic.gdx.math.Intersector;
+import com.badlogic.gdx.math.Vector3;
+import com.badlogic.gdx.math.collision.BoundingBox;
+import com.badlogic.gdx.math.collision.Ray;
+
+public class DecalPlayer {
+ private final TextureRegion[] regions;
+ private int regionIndex;
+ private final Decal decal;
+ private final BoundingBox box;
+ private final MaxonSavegame savegame;
+
+ public DecalPlayer(MaxonSavegame savegame, TextureRegion[] regions) {
+ this.savegame = savegame;
+
+ this.regions = regions;
+ this.regionIndex = 0;
+
+ this.decal = Decal.newDecal(this.regions[this.regionIndex]);
+ this.decal.setScale(0.025f);
+ this.decal.setPosition(2.0f, 1.75f, 2.0f);
+
+ float width = this.decal.getWidth() / (this.decal.getScaleX() * 1000f);
+ float height = this.decal.getHeight() / (this.decal.getScaleY() * 1000f);
+
+ Vector3 position = this.decal.getPosition();
+ Vector3 minBox = new Vector3(position.x - width / 3, position.y - height / 3, position.z - width / 3);
+ Vector3 maxBox = new Vector3(position.x + width / 3, position.y + height / 3, position.z + width / 3);
+
+ this.box = new BoundingBox(minBox, maxBox);
+ }
+
+ public void render(Camera camera) {
+ checkCollisions(camera);
+ }
+
+ private void checkCollisions(Camera camera) {
+ Ray ray = null;
+
+ if (Gdx.input.justTouched()) {
+ ray = camera.getPickRay(Gdx.input.getX(), Gdx.input.getY());
+ }
+
+ if (ray == null) {
+ return;
+ }
+
+ Vector3 intersection = new Vector3();
+
+ if (Intersector.intersectRayBounds(ray, box, intersection)) {
+ updateTextureRegion();
+ savegame.points++;
+ }
+ }
+
+ private void updateTextureRegion() {
+ this.regionIndex++;
+
+ if (this.regions[this.regionIndex] == null) {
+ this.regionIndex = 0;
+ }
+
+ this.decal.setTextureRegion(this.regions[this.regionIndex]);
+ }
+
+ public Decal getDecal() {
+ return this.decal;
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/player/MaxonItem.java b/core/src/kz/ilotterytea/maxon/player/MaxonItem.java
new file mode 100644
index 0000000..50a9f91
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/player/MaxonItem.java
@@ -0,0 +1,23 @@
+package kz.ilotterytea.maxon.player;
+
+import kz.ilotterytea.maxon.ui.AnimatedImage;
+
+public class MaxonItem {
+ public int id;
+ public String name;
+ public String desc;
+ public AnimatedImage icon;
+ public MaxonItemEnum type;
+ public float price;
+ public float multiplier;
+
+ public MaxonItem(int id, String name, String desc, AnimatedImage icon, MaxonItemEnum type, float price, float multiplier) {
+ this.id = id;
+ this.name = name;
+ this.desc = desc;
+ this.icon = icon;
+ this.type = type;
+ this.price = price;
+ this.multiplier = multiplier;
+ }
+} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/player/MaxonItemEnum.java b/core/src/kz/ilotterytea/maxon/player/MaxonItemEnum.java
new file mode 100644
index 0000000..12018be
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/player/MaxonItemEnum.java
@@ -0,0 +1,7 @@
+package kz.ilotterytea.maxon.player;
+
+public enum MaxonItemEnum {
+ DUMMY,
+ BUFF,
+ SLAVE
+}
diff --git a/core/src/kz/ilotterytea/maxon/player/MaxonItemRegister.java b/core/src/kz/ilotterytea/maxon/player/MaxonItemRegister.java
new file mode 100644
index 0000000..7b7bc33
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/player/MaxonItemRegister.java
@@ -0,0 +1,39 @@
+package kz.ilotterytea.maxon.player;
+
+import kz.ilotterytea.maxon.ui.AnimatedImage;
+
+import java.util.ArrayList;
+
+public class MaxonItemRegister {
+ private static ArrayList<MaxonItem> items = new ArrayList<>();
+
+ public static void register(
+ int id,
+ String name,
+ String desc,
+ AnimatedImage icon,
+ MaxonItemEnum type,
+ float price,
+ float multiplier
+ ) {
+ items.add(new MaxonItem(id, name, desc, icon, type, price, multiplier));
+ }
+
+ public static void clear() { items.clear(); }
+
+ public static void unRegister(
+ int id
+ ) {
+ items.remove(id);
+ }
+
+ public static ArrayList<MaxonItem> getItems() { return items; }
+ public static MaxonItem get(int id) {
+ for (MaxonItem item : items) {
+ if (item.id == id) {
+ return item;
+ }
+ }
+ return null;
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/player/MaxonPlayer.java b/core/src/kz/ilotterytea/maxon/player/MaxonPlayer.java
new file mode 100644
index 0000000..7ba5f49
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/player/MaxonPlayer.java
@@ -0,0 +1,26 @@
+package kz.ilotterytea.maxon.player;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+public class MaxonPlayer implements Serializable {
+ public float points;
+ public float multiplier;
+ public ArrayList<Integer> purchasedItems;
+
+ public MaxonPlayer() {
+ this.points = 0;
+ this.multiplier = 1.2f;
+ this.purchasedItems = new ArrayList<>();
+ }
+
+ public void load(MaxonPlayer player) {
+ if (player != null) {
+ this.points = player.points;
+ this.multiplier = player.multiplier;
+
+ this.purchasedItems.clear();
+ this.purchasedItems.addAll(player.purchasedItems);
+ }
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/player/MaxonSavegame.java b/core/src/kz/ilotterytea/maxon/player/MaxonSavegame.java
new file mode 100644
index 0000000..e0fe34c
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/player/MaxonSavegame.java
@@ -0,0 +1,40 @@
+package kz.ilotterytea.maxon.player;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * Save-game data class.
+ * @author NotDankEnough
+ * @since Alpha 1.2 (Oct 02, 2022)
+ */
+public class MaxonSavegame implements Serializable {
+ /** Earned Squish Points. */
+ public long points = 0L;
+ /** Multiplier. */
+ public long multiplier = 1;
+
+ /** Home inventory. */
+ public ArrayList<Integer> inv = new ArrayList<>();
+ /** Outside Inventory. */
+ public ArrayList<Integer> outInv = new ArrayList<>();
+
+ /** Seed. */
+ public long seed = System.currentTimeMillis();
+
+ /** Player name. */
+ public String name = System.getProperty("user.name");
+ /** Pet name. */
+ public String petName = "Maxon";
+ /** Pet ID. */
+ public byte petId = 0;
+
+ /** Elapsed time from game start. */
+ public long elapsedTime = 0;
+
+ /** Last timestamp when save game was used. */
+ public long lastTimestamp = System.currentTimeMillis();
+
+ /** Location. */
+ public short roomId = 0;
+} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/player/utils/PetUtils.kt b/core/src/kz/ilotterytea/maxon/player/utils/PetUtils.kt
new file mode 100644
index 0000000..31d3720
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/player/utils/PetUtils.kt
@@ -0,0 +1,42 @@
+package kz.ilotterytea.maxon.player.utils
+
+import com.badlogic.gdx.assets.AssetManager
+import com.badlogic.gdx.graphics.Texture
+import kz.ilotterytea.maxon.anim.SpriteUtils
+import kz.ilotterytea.maxon.ui.AnimatedImage
+
+/**
+ * Utilities for some operations with pets.
+ */
+class PetUtils {
+ companion object {
+ @JvmStatic
+ /**
+ * Get animated image of pet by its ID.
+ * */
+ fun animatedImageById(assetManager: AssetManager, id: Int) : AnimatedImage {
+ val img: AnimatedImage
+
+ when (id) {
+ // Maxon:
+ 0 -> img = AnimatedImage(SpriteUtils.splitToTextureRegions(
+ assetManager.get(
+ "sprites/sheet/loadingCircle.png",
+ Texture::class.java
+ ),
+ 112, 112, 10, 5
+ ))
+ // Maxon:
+ else -> img = AnimatedImage(SpriteUtils.splitToTextureRegions(
+ assetManager.get(
+ "sprites/sheet/loadingCircle.png",
+ Texture::class.java
+ ),
+ 112, 112, 10, 5
+ ))
+ }
+
+ return img
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/screens/GameScreen.java b/core/src/kz/ilotterytea/maxon/screens/GameScreen.java
new file mode 100644
index 0000000..a6dcace
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/screens/GameScreen.java
@@ -0,0 +1,603 @@
+package kz.ilotterytea.maxon.screens;
+
+import com.badlogic.gdx.*;
+import com.badlogic.gdx.audio.Music;
+import com.badlogic.gdx.graphics.*;
+import com.badlogic.gdx.graphics.g2d.TextureAtlas;
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.graphics.g3d.decals.CameraGroupStrategy;
+import com.badlogic.gdx.graphics.g3d.decals.Decal;
+import com.badlogic.gdx.graphics.g3d.decals.DecalBatch;
+import com.badlogic.gdx.math.Interpolation;
+import com.badlogic.gdx.math.Vector3;
+import com.badlogic.gdx.scenes.scene2d.InputEvent;
+import com.badlogic.gdx.scenes.scene2d.Stage;
+import com.badlogic.gdx.scenes.scene2d.actions.Actions;
+import com.badlogic.gdx.scenes.scene2d.ui.*;
+import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
+import com.badlogic.gdx.utils.Align;
+import com.badlogic.gdx.utils.Timer;
+import com.badlogic.gdx.utils.viewport.ScreenViewport;
+import kz.ilotterytea.maxon.MaxonConstants;
+import kz.ilotterytea.maxon.MaxonGame;
+import kz.ilotterytea.maxon.anim.SpriteUtils;
+import kz.ilotterytea.maxon.audio.Playlist;
+import kz.ilotterytea.maxon.inputprocessors.CrossProcessor;
+import kz.ilotterytea.maxon.player.DecalPlayer;
+import kz.ilotterytea.maxon.player.MaxonItem;
+import kz.ilotterytea.maxon.player.MaxonItemRegister;
+import kz.ilotterytea.maxon.player.MaxonSavegame;
+import kz.ilotterytea.maxon.screens.game.shop.ShopUI;
+import kz.ilotterytea.maxon.ui.*;
+import kz.ilotterytea.maxon.utils.math.Math;
+import kz.ilotterytea.maxon.utils.serialization.GameDataSystem;
+import com.rafaskoberg.gdx.typinglabel.TypingLabel;
+import net.mgsx.gltf.scene3d.attributes.PBRCubemapAttribute;
+import net.mgsx.gltf.scene3d.attributes.PBRTextureAttribute;
+import net.mgsx.gltf.scene3d.lights.DirectionalShadowLight;
+import net.mgsx.gltf.scene3d.lights.PointLightEx;
+import net.mgsx.gltf.scene3d.scene.Scene;
+import net.mgsx.gltf.scene3d.scene.SceneAsset;
+import net.mgsx.gltf.scene3d.scene.SceneManager;
+import net.mgsx.gltf.scene3d.scene.SceneSkybox;
+import net.mgsx.gltf.scene3d.utils.IBLBuilder;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+
+public class GameScreen implements Screen, InputProcessor {
+ final MaxonGame game;
+ final int slotId;
+ final long playTimestamp;
+ boolean isShopping = false, isInventoryEnabled = false;
+
+ MaxonSavegame player;
+
+ Stage stage;
+ Skin skin;
+
+ TextureAtlas mainAtlas;
+
+ Label pointsLabel, multiplierLabel;
+ AnimatedImage cat;
+ AnimatedImageButton maxon;
+
+ Table boardTable, quickTable;
+
+ Dialog notEnoughPointsDialog;
+
+ ArrayList<MaxonItem> items;
+ Map<Integer, Integer> invItems;
+
+ MovingChessBackground bg;
+ Playlist playlist;
+
+ private ShopUI shopUI;
+
+ private SceneManager sceneManager;
+ private PerspectiveCamera camera;
+
+ private DecalBatch decalBatch;
+ private ArrayList<Decal> decals;
+ private DecalPlayer decalPlayer;
+
+ private final MaxonSavegame savegame;
+
+ public GameScreen(MaxonGame game, MaxonSavegame sav, int slotId) throws IOException, ClassNotFoundException {
+ this.savegame = sav;
+
+ this.game = game;
+ this.slotId = slotId;
+ this.playTimestamp = System.currentTimeMillis();
+
+ create3D();
+
+ decalBatch = new DecalBatch(new CameraGroupStrategy(camera));
+ decals = new ArrayList<>();
+
+ TextureRegion[] playerTextureRegions = SpriteUtils.splitToTextureRegions(game.assetManager.get("sprites/sheet/loadingCircle.png", Texture.class), 112, 112, 10, 5);
+ decalPlayer = new DecalPlayer(sav, playerTextureRegions);
+ decals.add(decalPlayer.getDecal());
+
+ playlist = new Playlist(
+ game.assetManager.get("mus/game/onwards.wav", Music.class),
+ game.assetManager.get("mus/game/paris.wav", Music.class),
+ game.assetManager.get("mus/game/adieu.wav", Music.class),
+ game.assetManager.get("mus/game/shopping_spree.wav", Music.class)
+ );
+ playlist.setShuffleMode(true);
+ if (game.prefs.getBoolean("music", true)) playlist.next();
+
+ player = sav;
+ items = new ArrayList<>();
+
+ createStageUI();
+
+ Gdx.input.setInputProcessor(new InputMultiplexer(this, new CrossProcessor(), stage));
+ }
+
+ @Override
+ public void show() {
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ GameDataSystem.save(player, "latest.sav");
+ }
+ }, 10, 10);
+
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ float multiplier = 0;
+
+ for (MaxonItem item : items) {
+ multiplier += item.multiplier;
+ }
+
+ player.points += multiplier;
+
+ final TypingLabel label = new TypingLabel(game.locale.FormattedText("game.newPoint", MaxonConstants.DECIMAL_FORMAT.format(player.multiplier)), skin, "default");
+
+ label.setPosition(
+ maxon.getX(),
+ maxon.getY() + maxon.getHeight()
+ );
+
+ label.setWidth(maxon.getWidth());
+
+ label.setAlignment(Align.center);
+
+ label.addAction(Actions.parallel(
+ Actions.fadeOut(5f),
+ Actions.moveTo(
+ label.getX(), label.getY() + Math.getRandomNumber(10, 156), 5f, Interpolation.exp5Out)
+ ));
+
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ label.remove();
+ }
+ }, 10f);
+
+ stage.addActor(label);
+ }
+ }, 5, 5);
+
+ // Random gifts:
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ final ImageButton gift = new ImageButton(skin, "gift");
+ gift.setPosition(stage.getWidth() + gift.getWidth(), Math.getRandomNumber((int) gift.getHeight(), (int) stage.getHeight() - (int) gift.getHeight()));
+ gift.addAction(
+ Actions.repeat(
+ 3,
+ Actions.sequence(
+ Actions.moveTo(-gift.getWidth(), gift.getY(), 15f, Interpolation.linear),
+ Actions.moveTo(stage.getWidth() + gift.getWidth(), Math.getRandomNumber((int) gift.getHeight(), (int) stage.getHeight() - (int) gift.getHeight()), 15f, Interpolation.linear)
+ )
+ )
+ );
+
+
+ gift.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ int giftId = Math.getRandomNumber(0, 25);
+ final TypingLabel label = new TypingLabel(game.locale.TranslatableText("gifts.empty"), skin);
+
+ switch (giftId) {
+ // Points
+ case 0:
+ int randPoints = Math.getRandomNumber(150, 3000);
+ label.setText(game.locale.FormattedText("gifts.points", String.valueOf(randPoints)));
+ player.points += randPoints;
+ break;
+
+ // Multiplier
+ case 1:
+ int randMp = Math.getRandomNumber(1, 10);
+ label.setText(game.locale.FormattedText("gifts.multiplier", String.valueOf(randMp)));
+ player.multiplier += randMp;
+ break;
+
+
+ // Random pet
+ case 2:
+ int randPet = Math.getRandomNumber(0, 1);
+ assert MaxonItemRegister.get(randPet) != null;
+ String name = MaxonItemRegister.get(randPet).name;
+ label.setText(game.locale.FormattedText("gifts.new_pet", name));
+ player.inv.add(randPet);
+ if (invItems.containsKey(randPet)) {
+ invItems.put(randPet, invItems.get(randPet) + 1);
+ } else {
+ invItems.put(randPet, 1);
+ }
+ break;
+ // Default
+ default:
+ break;
+ }
+
+ label.setPosition(
+ gift.getX(),
+ gift.getY()
+ );
+
+ label.addAction(Actions.sequence(
+ Actions.delay(3f),
+ Actions.fadeOut(2f)
+ ));
+
+ stage.addActor(label);
+ gift.remove();
+
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ label.remove();
+ }
+ }, 5f);
+ }
+ });
+
+ stage.addActor(gift);
+ }
+ }, 600, 600);
+
+ camera.update();
+ render(Gdx.graphics.getDeltaTime());
+ }
+
+ @Override
+ public void render(float delta) {
+ Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
+
+ if (game.prefs.getBoolean("music", true) && !playlist.getPlayingNow().isPlaying()) {
+ playlist.next();
+ }
+
+ // i've temporarily commented it all out while i set up 3d
+ //game.batch.begin();
+
+ //bg.draw(game.batch);
+
+ //game.batch.end();
+
+ // Update the points label:
+ //pointsLabel.setText(game.locale.FormattedText("game.points",
+ // MaxonConstants.DECIMAL_FORMAT.format(player.points)
+ //));
+
+ // Update the multiplier label:
+ //multiplierLabel.setText(game.locale.FormattedText("game.multiplier",
+ // MaxonConstants.DECIMAL_FORMAT.format(player.multiplier)
+ //));
+
+ // Render 3D
+ sceneManager.update(Gdx.graphics.getDeltaTime());
+ sceneManager.render();
+
+ this.decalPlayer.render(this.camera);
+
+ for (Decal decal : this.decals) {
+ decal.lookAt(this.camera.position, this.camera.up);
+ this.decalBatch.add(decal);
+ }
+
+ this.decalBatch.flush();
+
+ stage.act(delta);
+ stage.draw();
+
+ shopUI.render();
+ }
+
+ @Override
+ public void resize(int width, int height) {
+ this.stage.getViewport().update(width, height, true);
+ sceneManager.updateViewport(width, height);
+ this.shopUI.update();
+ }
+
+ private void showShop() {
+ // - - - - - - S H O P T A B L E - - - - - - :
+ final Table shopTable = new Table(skin);
+ shopTable.setBackground("bg");
+ shopTable.setSize(stage.getWidth() - 20f, stage.getHeight() - (boardTable.getHeight() + quickTable.getHeight() + 20f));
+ shopTable.setPosition(10f, quickTable.getHeight() + 10f);
+ shopTable.align(Align.top | Align.center);
+
+ stage.addActor(shopTable);
+
+ // Header table:
+ Table headShopTable = new Table();
+ shopTable.add(headShopTable).width(shopTable.getWidth()).row();
+
+ // - - - S H O P T I T L E - - -:
+ Label shopTitle = new Label(game.locale.TranslatableText("game.petShop"), skin);
+ headShopTable.add(shopTitle).expandX();
+
+ // - - - C L O S E B U T T O N - - - :
+ TextButton closeButton = new TextButton("X", skin);
+
+ closeButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ shopTable.remove();
+ isShopping = !isShopping;
+ }
+ });
+
+ headShopTable.add(closeButton).row();
+
+ // - - - S H O P C O N T E N T - - - :
+ Table contentTable = new Table();
+
+ // Adding items to shop:
+ for (final MaxonItem item : MaxonItemRegister.getItems()) {
+ PurchaseItem p_item = new PurchaseItem(
+ skin,
+ item
+ );
+
+ p_item.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ if (player.points < item.price) {
+ notEnoughPointsDialog.show(stage);
+
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ notEnoughPointsDialog.hide(Actions.fadeOut(2f, Interpolation.smoother));
+ }
+ }, 2);
+
+ return;
+ }
+
+ player.points -= item.price;
+ player.multiplier += item.multiplier;
+ player.inv.add(item.id);
+
+ if (invItems.containsKey(item.id)) {
+ invItems.put(item.id, invItems.get(item.id) + 1);
+ } else {
+ invItems.put(item.id, 1);
+ }
+ }
+ });
+
+ contentTable.add(p_item).pad(6f).width(shopTable.getWidth()).row();
+ }
+
+ // Scroll panel for content table:
+ ScrollPane contentPane = new ScrollPane(contentTable);
+ contentPane.setScrollingDisabled(true, false);
+ shopTable.add(contentPane);
+ }
+
+ private void showInventory() {
+ // - - - - - - I N V E N T O R Y T A B L E - - - - - - :
+ final Table inventoryTable = new Table(skin);
+ inventoryTable.setBackground("bg");
+ inventoryTable.setSize(stage.getWidth() - 20f, stage.getHeight() - (boardTable.getHeight() + quickTable.getHeight() + 20f));
+ inventoryTable.setPosition(10f, quickTable.getHeight() + 10f);
+ inventoryTable.align(Align.top | Align.center);
+
+ stage.addActor(inventoryTable);
+
+ // Header table:
+ Table headInventoryTable = new Table();
+ inventoryTable.add(headInventoryTable).width(inventoryTable.getWidth()).row();
+
+ // - - - S H O P T I T L E - - -:
+ Label inventoryTitle = new Label(game.locale.TranslatableText("game.inventory.title"), skin);
+ headInventoryTable.add(inventoryTitle).expandX();
+
+ // - - - C L O S E B U T T O N - - - :
+ TextButton closeButton = new TextButton("X", skin);
+
+ closeButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ inventoryTable.remove();
+ isInventoryEnabled = !isInventoryEnabled;
+ }
+ });
+
+ headInventoryTable.add(closeButton).row();
+
+ // - - - I N V E N T O R Y C O N T E N T - - - :
+ Table contentTable = new Table();
+ contentTable.align(Align.left);
+
+ // Adding items to inventory:
+ for (int i = 0; i < invItems.keySet().size(); i++) {
+ MaxonItem item = MaxonItemRegister.get(i);
+
+ if (item != null) {
+ InventoryAnimatedItem invItem = new InventoryAnimatedItem(item, skin, invItems.get(i));
+ Cell<InventoryAnimatedItem> cell = contentTable.add(invItem).size(64, 64).pad(5f);
+
+ if (i != 0 && i % (inventoryTable.getWidth() / 69f) == 0) {
+ cell.row();
+ }
+ }
+ };
+
+ // Scroll panel for content table:
+ ScrollPane contentPane = new ScrollPane(contentTable);
+ contentPane.setScrollingDisabled(true, false);
+ inventoryTable.add(contentPane);
+ }
+
+ @Override public void pause() {}
+
+ @Override public void resume() {}
+
+ @Override public void hide() {
+ playlist.getPlayingNow().stop();
+ dispose();
+ }
+
+ @Override
+ public void dispose() {
+ stage.clear();
+ }
+
+ @Override
+ public boolean keyDown(int keycode) {
+ if (Gdx.input.isKeyPressed(Input.Keys.ESCAPE)) {
+ player.lastTimestamp = System.currentTimeMillis();
+ player.elapsedTime = (System.currentTimeMillis() - playTimestamp) + player.elapsedTime;
+ GameDataSystem.save(player, String.format("0%s.maxon", (slotId >= 0) ? slotId : "latest"));
+
+ game.setScreen(new MenuScreen(game));
+ dispose();
+ }
+ //if (Gdx.input.isKeyPressed(Input.Keys.SPACE) || Gdx.input.isKeyPressed(Input.Keys.UP)) {
+ // displayPointIncrease();
+ //}
+ return false;
+ }
+
+ private void displayPointIncrease() {
+ cat.nextFrame();
+ maxon.setDrawable(cat.getDrawable());
+
+ player.points += player.multiplier;
+
+ final TypingLabel label = new TypingLabel(game.locale.FormattedText("game.newPoint", MaxonConstants.DECIMAL_FORMAT.format(player.multiplier)), skin, "default");
+
+ label.setPosition(
+ maxon.getX(),
+ maxon.getY() + maxon.getHeight()
+ );
+
+ label.setWidth(maxon.getWidth());
+
+ label.setAlignment(Align.center);
+
+ label.addAction(Actions.parallel(
+ Actions.fadeOut(5f),
+ Actions.moveTo(
+ label.getX(), label.getY() + Math.getRandomNumber(10, 156), 5f, Interpolation.exp5Out)
+ ));
+
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ label.remove();
+ }
+ }, 10f);
+
+ stage.addActor(label);
+ }
+
+ private void create3D() {
+ SceneAsset sceneAsset = game.assetManager.get("models/scenes/living_room.glb", SceneAsset.class);
+ Scene scene = new Scene(sceneAsset.scene);
+
+ sceneManager = new SceneManager();
+ sceneManager.addScene(scene);
+
+ camera = new PerspectiveCamera(60f, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
+ camera.near = 1f;
+ camera.far = 300f;
+ camera.position.set(-3f, 2f, -0.3f);
+ camera.rotate(256f, 0f, 1f, 0f);
+
+ camera.update();
+
+ sceneManager.setCamera(camera);
+
+ DirectionalShadowLight light = new DirectionalShadowLight(1024, 1024, 60f, 60f, 1f, 300f);
+ light.set(new Color(0xdcccffff), -1f, -0.8f, -0.2f);
+ light.intensity = 5f;
+ sceneManager.environment.add(light);
+ sceneManager.environment.shadowMap = light;
+
+ PointLightEx signLight = new PointLightEx();
+ signLight.set(Color.PINK, new Vector3(2f, 6f, 2f), 80f, 100f);
+
+ PointLightEx windowLight = new PointLightEx();
+ windowLight.set(Color.BLUE, new Vector3(-1.1f, 7.3f, 0.5f), 80f, 100f);
+
+ sceneManager.environment.add(windowLight, signLight);
+
+ // setup quick IBL (image based lighting)
+ IBLBuilder iblBuilder = IBLBuilder.createOutdoor(light);
+ Cubemap environmentCubemap = iblBuilder.buildEnvMap(1024);
+ Cubemap diffuseCubemap = iblBuilder.buildIrradianceMap(256);
+ Cubemap specularCubemap = iblBuilder.buildRadianceMap(10);
+ iblBuilder.dispose();
+
+ Texture brdfLUT = new Texture(Gdx.files.classpath("net/mgsx/gltf/shaders/brdfLUT.png"));
+
+ sceneManager.setAmbientLight(1f);
+ sceneManager.environment.set(new PBRTextureAttribute(PBRTextureAttribute.BRDFLUTTexture, brdfLUT));
+ sceneManager.environment.set(PBRCubemapAttribute.createSpecularEnv(specularCubemap));
+ sceneManager.environment.set(PBRCubemapAttribute.createDiffuseEnv(diffuseCubemap));
+
+ SceneSkybox skybox = new SceneSkybox(environmentCubemap);
+ sceneManager.setSkyBox(skybox);
+ }
+
+ private void createStageUI() {
+ this.stage = new Stage(new ScreenViewport());
+ this.skin = this.game.assetManager.get("MainSpritesheet.skin", Skin.class);
+ this.mainAtlas = this.game.assetManager.get("MainSpritesheet.atlas", TextureAtlas.class);
+
+ this.shopUI = new ShopUI(savegame, this.stage, this.skin, this.mainAtlas);
+
+ shopUI.createShopTitleUI();
+ shopUI.createShopControlUI();
+ shopUI.createShopListUI();
+ shopUI.createSavegameUI();
+ }
+
+ @Override
+ public boolean keyUp(int keycode) {
+ return false;
+ }
+
+ @Override
+ public boolean keyTyped(char character) {
+ return false;
+ }
+
+ @Override
+ public boolean touchDown(int screenX, int screenY, int pointer, int button) {
+ return false;
+ }
+
+ @Override
+ public boolean touchUp(int screenX, int screenY, int pointer, int button) {
+ return false;
+ }
+
+ @Override
+ public boolean touchCancelled(int i, int i1, int i2, int i3) {
+ return false;
+ }
+
+ @Override
+ public boolean touchDragged(int screenX, int screenY, int pointer) {
+ return false;
+ }
+
+ @Override
+ public boolean mouseMoved(int screenX, int screenY) {
+ return false;
+ }
+
+ @Override
+ public boolean scrolled(float amountX, float amountY) {
+ return false;
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/screens/MenuScreen.java b/core/src/kz/ilotterytea/maxon/screens/MenuScreen.java
new file mode 100644
index 0000000..ef75046
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/screens/MenuScreen.java
@@ -0,0 +1,380 @@
+package kz.ilotterytea.maxon.screens;
+
+import com.badlogic.gdx.*;
+import com.badlogic.gdx.audio.Music;
+import com.badlogic.gdx.files.FileHandle;
+import com.badlogic.gdx.graphics.*;
+import com.badlogic.gdx.graphics.g2d.TextureAtlas;
+import com.badlogic.gdx.math.Interpolation;
+import com.badlogic.gdx.math.Vector3;
+import com.badlogic.gdx.scenes.scene2d.Action;
+import com.badlogic.gdx.scenes.scene2d.InputEvent;
+import com.badlogic.gdx.scenes.scene2d.Stage;
+import com.badlogic.gdx.scenes.scene2d.actions.Actions;
+import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction;
+import com.badlogic.gdx.scenes.scene2d.ui.*;
+import com.badlogic.gdx.scenes.scene2d.ui.Button;
+import com.badlogic.gdx.scenes.scene2d.ui.Image;
+import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
+import com.badlogic.gdx.utils.Align;
+import com.badlogic.gdx.utils.Timer;
+import com.badlogic.gdx.utils.viewport.ScreenViewport;
+import kz.ilotterytea.maxon.MaxonConstants;
+import kz.ilotterytea.maxon.MaxonGame;
+import kz.ilotterytea.maxon.player.MaxonSavegame;
+import kz.ilotterytea.maxon.ui.*;
+import kz.ilotterytea.maxon.utils.I18N;
+import kz.ilotterytea.maxon.utils.serialization.GameDataSystem;
+import net.mgsx.gltf.scene3d.attributes.PBRCubemapAttribute;
+import net.mgsx.gltf.scene3d.attributes.PBRTextureAttribute;
+import net.mgsx.gltf.scene3d.lights.DirectionalShadowLight;
+import net.mgsx.gltf.scene3d.lights.PointLightEx;
+import net.mgsx.gltf.scene3d.scene.Scene;
+import net.mgsx.gltf.scene3d.scene.SceneAsset;
+import net.mgsx.gltf.scene3d.scene.SceneManager;
+import net.mgsx.gltf.scene3d.utils.IBLBuilder;
+
+import java.util.ArrayList;
+
+public class MenuScreen implements Screen {
+ private final MaxonGame game;
+
+ private final Stage stage;
+ private final Music menuMusic;
+
+ MaxonSavegame sav;
+
+ private SceneManager sceneManager;
+ private PerspectiveCamera camera;
+
+ public MenuScreen(final MaxonGame game) {
+ this.game = game;
+
+ // Stage and skin:
+ this.stage = new Stage(new ScreenViewport());
+ this.stage.addAction(Actions.sequence(Actions.alpha(0.0f), Actions.alpha(1.0f, 1f)));
+
+ Skin skin = game.assetManager.get("MainSpritesheet.skin", Skin.class);
+ Skin widgetSkin = game.assetManager.get("sprites/gui/widgets.skin", Skin.class);
+ TextureAtlas brandAtlas = game.assetManager.get("sprites/gui/brand.atlas", TextureAtlas.class);
+ TextureAtlas widgetAtlas = game.assetManager.get("sprites/gui/widgets.atlas", TextureAtlas.class);
+
+ Skin friendsSkin = game.assetManager.get("sprites/gui/friends.skin", Skin.class);
+
+ sav = GameDataSystem.load("00.maxon");
+
+ // Main Menu music:
+ this.menuMusic = game.assetManager.get("mus/menu/mus_menu_loop.ogg", Music.class);
+ menuMusic.setLooping(true);
+
+ // Tint the background
+ Image tintImage = new Image(skin, "tint");
+ tintImage.setFillParent(true);
+ this.stage.addActor(tintImage);
+
+ // - - - - - - U I - - - - - -
+ Table menuTable = new Table();
+ menuTable.setFillParent(true);
+
+ // - - - Brand - - -
+ Table brandTable = new Table();
+
+ Image logo = new Image(brandAtlas.findRegion("brand"));
+
+ logo.setOrigin(
+ logo.getWidth() / 2f,
+ logo.getHeight() / 2f
+ );
+
+ logo.addAction(
+ Actions.repeat(
+ RepeatAction.FOREVER,
+ Actions.sequence(
+ Actions.parallel(
+ Actions.rotateTo(-5f, 5f, Interpolation.smoother),
+ Actions.scaleTo(0.9f, 0.9f, 5f, Interpolation.smoother)
+ ),
+ Actions.parallel(
+ Actions.rotateTo(5f, 5f, Interpolation.smoother),
+ Actions.scaleTo(1.1f, 1.1f, 5f, Interpolation.smoother)
+ )
+ )
+ )
+ );
+
+ brandTable.add(logo);
+
+ // - - - Menu control (quit, options, etc.) - - -
+ Table controlTable = new Table(skin);
+ controlTable.align(Align.top | Align.center);
+ controlTable.pad(6f);
+
+ // Left part of menu control
+ Table leftGameControlTable = new Table();
+ leftGameControlTable.align(Align.left);
+
+ ImageButton quitButton = new ImageButton(widgetSkin, "quit");
+
+ quitButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ Gdx.app.exit();
+ }
+ });
+
+ leftGameControlTable.add(quitButton).padRight(12f);
+
+ // Right part of menu control
+ Table rightGameControlTable = new Table();
+ rightGameControlTable.align(Align.right);
+
+ // - - - D E V E L O P E R S H O W C A S E - - -
+ Image developerImage = new Image();
+ final int[] developerIndex = {MaxonConstants.GAME_DEVELOPERS.length};
+ developerImage.setSize(64, 64);
+
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ developerIndex[0]++;
+
+ if (developerIndex[0] >= MaxonConstants.GAME_DEVELOPERS.length) {
+ developerIndex[0] = 0;
+ }
+
+ String[] dev = MaxonConstants.GAME_DEVELOPERS[developerIndex[0]];
+
+ developerImage.clearActions();
+ developerImage.addAction(
+ Actions.sequence(
+ Actions.alpha(0.0f, 1f),
+ new Action() {
+ @Override
+ public boolean act(float delta) {
+ developerImage.setDrawable(friendsSkin, dev[0]);
+ return true;
+ }
+ },
+ Actions.alpha(1.0f, 1f)
+ )
+ );
+
+ developerImage.clearListeners();
+ developerImage.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ Gdx.net.openURI(dev[1]);
+ }
+ });
+ }
+ }, 0, 5);
+
+ rightGameControlTable.add(developerImage).padRight(16f);
+
+ // Localization
+ String[] fh4Locale = game.locale.getFileHandle().nameWithoutExtension().split("_");
+ String localeButtonStyleName = "locale_" + fh4Locale[0];
+ ImageButton localeButton = new ImageButton(widgetSkin, localeButtonStyleName);
+
+ localeButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+
+ int index = 0;
+ ArrayList<FileHandle> fhArray = new ArrayList<>();
+ fhArray.add(MaxonConstants.FILE_RU_RU);
+ fhArray.add(MaxonConstants.FILE_EN_US);
+
+ if (fhArray.indexOf(game.locale.getFileHandle()) + 1 < fhArray.size()) {
+ index = fhArray.indexOf(game.locale.getFileHandle()) + 1;
+ }
+
+ FileHandle fhNext = fhArray.get(index);
+
+ game.locale = new I18N(fhNext);
+ game.prefs.putString("lang", fhNext.nameWithoutExtension());
+ game.prefs.flush();
+
+ game.setScreen(new SplashScreen(game));
+ menuMusic.stop();
+ }
+ });
+
+
+ rightGameControlTable.add(localeButton).padRight(16f);
+
+ // Music button
+ String musicButtonStyleName;
+
+ if (game.prefs.getBoolean("music")) {
+ musicButtonStyleName = "music_on";
+ menuMusic.play();
+ } else {
+ musicButtonStyleName = "music_off";
+ }
+
+ ImageButton musicButton = new ImageButton(widgetSkin, musicButtonStyleName);
+ musicButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+
+ Button.ButtonStyle style;
+
+ if (game.prefs.getBoolean("music")) {
+ style = widgetSkin.get("music_off", ImageButton.ImageButtonStyle.class);
+ menuMusic.pause();
+ } else {
+ style = widgetSkin.get("music_on", ImageButton.ImageButtonStyle.class);
+ menuMusic.play();
+ }
+
+ game.prefs.putBoolean("music", !game.prefs.getBoolean("music"));
+ game.prefs.flush();
+
+ musicButton.setStyle(style);
+ }
+ });
+ rightGameControlTable.add(musicButton).padRight(16f);
+
+ // Resolution button
+ String resolutionButtonStyleName;
+
+ if (game.prefs.getBoolean("fullscreen")) {
+ resolutionButtonStyleName = "windowed";
+ } else {
+ resolutionButtonStyleName = "fullscreen";
+ }
+
+ ImageButton resolutionButton = new ImageButton(widgetSkin, resolutionButtonStyleName);
+ resolutionButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+
+ Button.ButtonStyle style;
+
+ if (game.prefs.getBoolean("fullscreen")) {
+ style = widgetSkin.get("fullscreen", ImageButton.ImageButtonStyle.class);
+ Gdx.graphics.setWindowedMode(game.prefs.getInteger("width", 800), game.prefs.getInteger("height", 600));
+ } else {
+ style = widgetSkin.get("windowed", ImageButton.ImageButtonStyle.class);
+ Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
+ }
+
+ game.prefs.putBoolean("fullscreen", !game.prefs.getBoolean("fullscreen"));
+ game.prefs.flush();
+
+ resolutionButton.setStyle(style);
+ }
+ });
+ rightGameControlTable.add(resolutionButton);
+
+ controlTable.add(leftGameControlTable).grow();
+ controlTable.add(rightGameControlTable).grow();
+
+ // - - - Savegame - - -
+ Table savegameTable = new Table();
+ SavegameWidget info = new SavegameWidget(this.game, skin, stage, sav);
+
+ savegameTable.add(info).minSize(640f, 240f);
+
+ // Adding tables into the main UI table
+ menuTable.add(brandTable).grow().row();
+ menuTable.add(savegameTable).grow().row();
+ menuTable.add(controlTable).growX();
+
+ this.stage.addActor(menuTable);
+
+ create3D();
+ Gdx.input.setInputProcessor(stage);
+ }
+
+ @Override public void show() {
+ if (game.prefs.getBoolean("music", true)) menuMusic.play();
+
+ // Start to render:
+ render(Gdx.graphics.getDeltaTime());
+ }
+
+ @Override
+ public void render(float delta) {
+ Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
+
+ sceneManager.update(delta);
+ sceneManager.render();
+
+ camera.rotate(2 * delta, 0, 1, 0);
+ camera.update();
+
+ stage.act(delta);
+ stage.draw();
+ }
+
+ @Override
+ public void resize(int width, int height) {
+ stage.getViewport().update(width, height, true);
+ sceneManager.updateViewport(width, height);
+ }
+
+ @Override public void pause() {}
+ @Override public void resume() {}
+ @Override public void hide() {
+ menuMusic.stop();
+ dispose();
+ }
+ @Override public void dispose() {
+ stage.dispose();
+ }
+
+ private void create3D() {
+ SceneAsset sceneAsset = game.assetManager.get("models/scenes/living_room.glb", SceneAsset.class);
+ Scene scene = new Scene(sceneAsset.scene);
+
+ sceneManager = new SceneManager();
+ sceneManager.addScene(scene);
+
+ camera = new PerspectiveCamera(60f, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
+ camera.near = 1f;
+ camera.far = 300f;
+ camera.position.set(0f, 5f, 0f);
+
+ camera.update();
+
+ sceneManager.setCamera(camera);
+
+ DirectionalShadowLight light = new DirectionalShadowLight(1024, 1024, 60f, 60f, 1f, 300f);
+ light.set(new Color(0xdcccffff), -1f, -0.8f, -0.2f);
+ light.intensity = 5f;
+ sceneManager.environment.add(light);
+ sceneManager.environment.shadowMap = light;
+
+ PointLightEx signLight = new PointLightEx();
+
+ if (sav != null) {
+ signLight.set(Color.PINK, new Vector3(2f, 6f, 2f), 80f, 100f);
+ }
+
+ PointLightEx windowLight = new PointLightEx();
+ windowLight.set(Color.BLUE, new Vector3(-1.1f, 7.3f, 0.5f), 80f, 100f);
+
+ sceneManager.environment.add(windowLight, signLight);
+
+ // setup quick IBL (image based lighting)
+ IBLBuilder iblBuilder = IBLBuilder.createOutdoor(light);
+ Cubemap environmentCubemap = iblBuilder.buildEnvMap(1024);
+ Cubemap diffuseCubemap = iblBuilder.buildIrradianceMap(256);
+ Cubemap specularCubemap = iblBuilder.buildRadianceMap(10);
+ iblBuilder.dispose();
+
+ Texture brdfLUT = new Texture(Gdx.files.classpath("net/mgsx/gltf/shaders/brdfLUT.png"));
+
+ sceneManager.setAmbientLight(1f);
+ sceneManager.environment.set(new PBRTextureAttribute(PBRTextureAttribute.BRDFLUTTexture, brdfLUT));
+ sceneManager.environment.set(PBRCubemapAttribute.createSpecularEnv(specularCubemap));
+ sceneManager.environment.set(PBRCubemapAttribute.createDiffuseEnv(diffuseCubemap));
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/screens/SplashScreen.java b/core/src/kz/ilotterytea/maxon/screens/SplashScreen.java
new file mode 100644
index 0000000..f381bb8
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/screens/SplashScreen.java
@@ -0,0 +1,102 @@
+package kz.ilotterytea.maxon.screens;
+
+import com.badlogic.gdx.*;
+import com.badlogic.gdx.graphics.GL20;
+import com.badlogic.gdx.graphics.g2d.TextureAtlas;
+import com.badlogic.gdx.scenes.scene2d.Action;
+import com.badlogic.gdx.scenes.scene2d.Stage;
+import com.badlogic.gdx.scenes.scene2d.actions.Actions;
+import com.badlogic.gdx.scenes.scene2d.ui.Image;
+import com.badlogic.gdx.scenes.scene2d.ui.ProgressBar;
+import com.badlogic.gdx.scenes.scene2d.ui.Skin;
+import com.badlogic.gdx.scenes.scene2d.ui.Table;
+import com.badlogic.gdx.utils.Align;
+import com.badlogic.gdx.utils.viewport.ScreenViewport;
+import kz.ilotterytea.maxon.utils.AssetLoading;
+import kz.ilotterytea.maxon.MaxonGame;
+
+public class SplashScreen implements Screen {
+ final MaxonGame game;
+
+ final Stage stage;
+ final Skin skin;
+
+ TextureAtlas brandAtlas;
+ Image dev;
+ ProgressBar bar;
+
+ private boolean assetsLoaded = false;
+
+ public SplashScreen(MaxonGame game) {
+ this.game = game;
+
+ this.stage = new Stage(new ScreenViewport());
+ this.skin = new Skin(Gdx.files.internal("MainSpritesheet.skin"));
+
+ Table logoTable = new Table();
+ logoTable.setFillParent(true);
+ logoTable.align(Align.center);
+
+ brandAtlas = new TextureAtlas(Gdx.files.internal("sprites/gui/ilotterytea.atlas"));
+
+ dev = new Image(brandAtlas.findRegion("devOld"));
+ dev.setSize(dev.getWidth() * 5f, dev.getHeight() * 5f);
+ logoTable.add(dev).size(dev.getWidth(), dev.getHeight()).padBottom(60f).row();
+
+ bar = new ProgressBar(0f, 100f, 1f, false, skin);
+ logoTable.add(bar).width(dev.getWidth());
+
+ stage.addActor(logoTable);
+
+ AssetLoading.setup(game.assetManager);
+ AssetLoading.queue(game.assetManager);
+ }
+
+ @Override public void show() {
+ render(Gdx.graphics.getDeltaTime());
+ }
+
+ private void update() {
+ if (game.assetManager.update() && !assetsLoaded) {
+ stage.addAction(
+ Actions.sequence(
+ Actions.alpha(0.0f, 1f),
+ new Action() {
+ @Override
+ public boolean act(float v) {
+ AssetLoading.registerItems(game.assetManager, game.locale);
+ game.setScreen(new MenuScreen(game));
+ dispose();
+ return false;
+ }
+ }
+ )
+ );
+ assetsLoaded = true;
+ }
+ }
+
+ @Override
+ public void render(float delta) {
+ Gdx.gl.glClearColor(1, 1, 1, 1);
+ Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
+
+ stage.draw();
+ stage.act(delta);
+
+ update();
+ bar.setValue(100f / (game.assetManager.getQueuedAssets() + 1));
+ }
+
+ @Override
+ public void resize(int width, int height) {
+ stage.getViewport().update(width, height, true);
+ }
+
+ @Override public void pause() {}
+ @Override public void resume() {}
+ @Override public void hide() { dispose(); }
+ @Override public void dispose() {
+ brandAtlas.dispose();
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/screens/game/shop/ShopMode.java b/core/src/kz/ilotterytea/maxon/screens/game/shop/ShopMode.java
new file mode 100644
index 0000000..d81e925
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/screens/game/shop/ShopMode.java
@@ -0,0 +1,6 @@
+package kz.ilotterytea.maxon.screens.game.shop;
+
+public enum ShopMode {
+ BUY,
+ SELL
+}
diff --git a/core/src/kz/ilotterytea/maxon/screens/game/shop/ShopMultiplier.java b/core/src/kz/ilotterytea/maxon/screens/game/shop/ShopMultiplier.java
new file mode 100644
index 0000000..361290a
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/screens/game/shop/ShopMultiplier.java
@@ -0,0 +1,17 @@
+package kz.ilotterytea.maxon.screens.game.shop;
+
+public enum ShopMultiplier {
+ X1(1),
+ X10(10),
+ ;
+
+ private final int multiplier;
+
+ ShopMultiplier(int multiplier) {
+ this.multiplier = multiplier;
+ }
+
+ public int getMultiplier() {
+ return multiplier;
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/screens/game/shop/ShopUI.java b/core/src/kz/ilotterytea/maxon/screens/game/shop/ShopUI.java
new file mode 100644
index 0000000..0282ea6
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/screens/game/shop/ShopUI.java
@@ -0,0 +1,249 @@
+package kz.ilotterytea.maxon.screens.game.shop;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.graphics.g2d.TextureAtlas;
+import com.badlogic.gdx.scenes.scene2d.InputEvent;
+import com.badlogic.gdx.scenes.scene2d.Stage;
+import com.badlogic.gdx.scenes.scene2d.ui.*;
+import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
+import com.badlogic.gdx.utils.Align;
+import kz.ilotterytea.maxon.player.MaxonItem;
+import kz.ilotterytea.maxon.player.MaxonItemRegister;
+import kz.ilotterytea.maxon.player.MaxonSavegame;
+import kz.ilotterytea.maxon.ui.PurchaseItem;
+import kz.ilotterytea.maxon.utils.math.Math;
+
+import java.util.ArrayList;
+
+public class ShopUI {
+ private final Table table, mainTable;
+ private final Skin skin;
+ private final TextureAtlas atlas;
+ private final ArrayList<MaxonItem> items;
+
+ private ShopMode mode;
+ private ShopMultiplier multiplier;
+
+ private final MaxonSavegame savegame;
+ private Label pointsLabel, multiplierLabel;
+
+ private final ArrayList<PurchaseItem> purchaseItems = new ArrayList<>();
+
+ public ShopUI(final MaxonSavegame savegame, Stage stage, Skin skin, TextureAtlas atlas) {
+ this.savegame = savegame;
+
+ this.skin = skin;
+ this.atlas = atlas;
+ this.mode = ShopMode.BUY;
+ this.multiplier = ShopMultiplier.X1;
+ this.items = MaxonItemRegister.getItems();
+
+ this.table = new Table(skin);
+ this.table.setBackground("board");
+
+ this.mainTable = new Table(this.skin);
+ mainTable.setFillParent(true);
+ mainTable.align(Align.center | Align.left);
+
+ mainTable.add(this.table).growY().width(Math.percentFromValue(25f, Gdx.graphics.getWidth()));
+ stage.addActor(mainTable);
+ }
+
+ public void createSavegameUI() {
+ Table table = new Table(this.skin);
+
+ table.align(Align.center | Align.left);
+ table.pad(10f);
+
+ // Setting up the points
+ Table pointsTable = new Table();
+
+ Image pointsImage = new Image(this.atlas.findRegion("points"));
+ this.pointsLabel = new Label(String.valueOf(savegame.points), this.skin);
+
+ pointsTable.add(pointsImage);
+ pointsTable.add(pointsLabel).padLeft(15f);
+
+ table.add(pointsTable).padBottom(10f).row();
+
+ // Setting up the multiplier
+ Table multiplierTable = new Table();
+
+ Image multiplierImage = new Image(this.atlas.findRegion("multiplier"));
+ this.multiplierLabel = new Label(String.format("%s/s", savegame.multiplier), this.skin);
+
+ multiplierTable.add(multiplierImage);
+ multiplierTable.add(multiplierLabel).padLeft(15f);
+
+ table.add(multiplierTable);
+
+ this.table.add(table).grow();
+ }
+
+ public void createShopTitleUI() {
+ Table table = new Table(this.skin);
+
+ table.align(Align.center);
+ table.pad(10f);
+
+ Label label = new Label("Store", skin);
+ table.add(label);
+
+ this.table.add(table).grow().row();
+ }
+
+ public void createShopControlUI() {
+ Table table = new Table(this.skin);
+
+ table.align(Align.center);
+ table.pad(10f);
+
+ // Mode changer
+ Table modeTable = new Table();
+
+ TextButton buyButton = new TextButton("Buy", this.skin);
+ buyButton.setDisabled(true);
+ modeTable.add(buyButton).growX().row();
+
+ TextButton sellButton = new TextButton("Sell", this.skin);
+ modeTable.add(sellButton).growX();
+
+ sellButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ mode = ShopMode.SELL;
+ sellButton.setDisabled(true);
+ buyButton.setDisabled(false);
+ }
+ });
+
+ buyButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ mode = ShopMode.BUY;
+ sellButton.setDisabled(false);
+ buyButton.setDisabled(true);
+ }
+ });
+
+ table.add(modeTable).grow();
+
+ // Multiplier changer
+ Table multiplierTable = new Table();
+
+ TextButton x1Button = new TextButton("1x", this.skin);
+ x1Button.setDisabled(true);
+ multiplierTable.add(x1Button).width(64f).height(64f).padRight(10f);
+
+ TextButton x10Button = new TextButton("10x", this.skin);
+ multiplierTable.add(x10Button).width(64f).height(64f);
+
+ x1Button.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ multiplier = ShopMultiplier.X1;
+ x1Button.setDisabled(true);
+ x10Button.setDisabled(false);
+ }
+ });
+
+ x10Button.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ multiplier = ShopMultiplier.X10;
+ x1Button.setDisabled(false);
+ x10Button.setDisabled(true);
+ }
+ });
+
+ table.add(multiplierTable).grow();
+
+ this.table.add(table).grow().row();
+ }
+
+ public void createShopListUI() {
+ Table table = new Table(this.skin);
+ table.setBackground("shop_list");
+
+ for (final MaxonItem item : this.items) {
+ PurchaseItem purchaseItem = new PurchaseItem(this.skin, item);
+ purchaseItem.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+
+ if (purchaseItem.isDisabled()) {
+ return;
+ }
+
+ if (mode == ShopMode.BUY) {
+ savegame.points -= (long) purchaseItem.getPrice();
+ for (int i = 0; i < multiplier.getMultiplier(); i++) {
+ savegame.inv.add(purchaseItem.getItem().id);
+ }
+ } else {
+ savegame.points += (long) purchaseItem.getPrice();
+ for (int i = 0; i < multiplier.getMultiplier(); i++) {
+ savegame.inv.remove(Integer.valueOf(purchaseItem.getItem().id));
+ }
+ }
+ }
+ });
+
+ purchaseItems.add(purchaseItem);
+ table.add(purchaseItem).growX().padBottom(5f).row();
+ }
+
+ ScrollPane scrollPane = new ScrollPane(table);
+ scrollPane.setScrollingDisabled(true, false);
+
+ Table scrollPaneTable = new Table(this.skin);
+ scrollPaneTable.setBackground("shop_list");
+ scrollPaneTable.pad(1f, 5f, 1f, 5f);
+ scrollPaneTable.add(scrollPane).grow();
+
+ this.table.add(scrollPaneTable).grow().row();
+ }
+
+ private void updatePurchaseItems() {
+ for (final PurchaseItem item : this.purchaseItems) {
+ int amount = (int) savegame.inv.stream().filter(c -> c == item.getItem().id).count();
+ double price = item.getItem().price * java.lang.Math.pow(1.15f, amount + multiplier.getMultiplier());
+
+ if (mode == ShopMode.SELL) {
+ price /= 4;
+ }
+
+ item.setPrice(price);
+
+ if (mode == ShopMode.BUY) {
+ if (price > savegame.points || savegame.points - price < 0) {
+ item.setDisabled(true);
+ } else if (item.isDisabled()) {
+ item.setDisabled(false);
+ }
+ } else {
+ if (amount - multiplier.getMultiplier() < 0) {
+ item.setDisabled(true);
+ } else if (item.isDisabled()) {
+ item.setDisabled(false);
+ }
+ }
+ }
+ }
+
+ public void render() {
+ this.pointsLabel.setText(String.valueOf(savegame.points));
+ this.multiplierLabel.setText(String.format("%s/s", savegame.multiplier));
+ updatePurchaseItems();
+ }
+
+ public void update() {
+ this.mainTable.clear();
+ this.mainTable.add(this.table).growY().width(Math.percentFromValue(30f, Gdx.graphics.getWidth()));
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/ui/AnimatedImage.java b/core/src/kz/ilotterytea/maxon/ui/AnimatedImage.java
new file mode 100644
index 0000000..097fb2b
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/AnimatedImage.java
@@ -0,0 +1,60 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.scenes.scene2d.ui.Image;
+import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
+import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
+import com.badlogic.gdx.utils.Disposable;
+
+public class AnimatedImage extends Image implements Disposable {
+ private final TextureRegion[] regions;
+ private int index = 0;
+
+ private boolean stopAnim = false;
+
+ public AnimatedImage(TextureRegion[] regions) {
+ super(regions[0]);
+ this.regions = regions;
+ }
+
+ @Override public void act(float delta) {
+ if (!stopAnim) {
+ if (index > regions.length - 1) {
+ index = 0;
+ }
+ if (regions[index + 1] == null) {
+ index = 0;
+ }
+ super.setDrawable(new TextureRegionDrawable(regions[index]));
+ index++;
+ }
+ super.act(delta);
+ }
+
+ public TextureRegion getFrame(int index) { return regions[index]; }
+ public int getIndex() { return index; }
+ public Drawable getDrawable() { return super.getDrawable(); }
+
+ public void nextFrame() {
+ index++;
+
+ if (index > regions.length - 1 || regions[index] == null) {
+ index = 0;
+ }
+
+ super.setDrawable(new TextureRegionDrawable(regions[index]));
+ }
+
+ public void disableAnim() { stopAnim = true; }
+ public void enableAnim() { stopAnim = false; }
+
+ public boolean isAnimationStopped() { return stopAnim; }
+
+ @Override public void dispose() {
+ for (TextureRegion reg : regions) {
+ if (reg != null) {
+ reg.getTexture().dispose();
+ }
+ }
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/ui/AnimatedImageButton.java b/core/src/kz/ilotterytea/maxon/ui/AnimatedImageButton.java
new file mode 100644
index 0000000..4972afd
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/AnimatedImageButton.java
@@ -0,0 +1,24 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
+import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
+
+public class AnimatedImageButton extends ImageButton {
+ public AnimatedImageButton(AnimatedImage image) {
+ super(image.getDrawable());
+ ImageButtonStyle style = new ImageButtonStyle();
+
+ style.up = image.getDrawable();
+ super.setStyle(style);
+ }
+
+ public void setDrawable(Drawable drawable) {
+ ImageButtonStyle style = new ImageButtonStyle();
+
+ style.up = drawable;
+ super.setStyle(style);
+ }
+
+ @Override public void act(float delta) {
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/ui/DebugInfo.kt b/core/src/kz/ilotterytea/maxon/ui/DebugInfo.kt
new file mode 100644
index 0000000..7329725
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/DebugInfo.kt
@@ -0,0 +1,56 @@
+package kz.ilotterytea.maxon.ui
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.Version
+import com.badlogic.gdx.scenes.scene2d.ui.Label
+import com.badlogic.gdx.scenes.scene2d.ui.Skin
+import com.badlogic.gdx.scenes.scene2d.ui.Table
+import com.badlogic.gdx.utils.Align
+import kz.ilotterytea.maxon.MaxonConstants
+import kz.ilotterytea.maxon.utils.I18N
+
+/**
+ * Debug information.
+ * @since a_1.0
+ * @author ilotterytea
+ */
+class DebugInfo(skin: Skin, locale: I18N) : Table() {
+ private val i18n = locale
+ private var c_fps: Label
+ private var c_mem: Label
+
+ init {
+ val rt = Runtime.getRuntime()
+ val usedmem = ((rt.totalMemory() - rt.freeMemory()) / 1024) / 1024
+ val totalmem = (rt.totalMemory() / 1024) / 1024
+
+ // Version info:
+ val ver = Label(i18n.FormattedText("debug.version", MaxonConstants.GAME_VERSION, Version.VERSION, System.getProperty("java.version")), skin, "debug")
+ ver.setAlignment(Align.left)
+ this.add(ver).fillX().row()
+
+ // Frames per second:
+ c_fps = Label(i18n.FormattedText("debug.c_fps", Gdx.graphics.framesPerSecond.toString()), skin, "debug")
+ c_fps.setAlignment(Align.left)
+ this.add(c_fps).fillX().row()
+
+ // Memory usage:
+ c_mem = Label(i18n.FormattedText("debug.c_mem", usedmem.toString(), totalmem.toString()), skin, "debug")
+ c_mem.setAlignment(Align.left)
+ this.add(c_mem).fillX().row()
+
+ this.align(Align.left)
+ this.skin = skin
+ this.background("tile_03")
+ }
+
+ override fun act(delta: Float) {
+ val rt = Runtime.getRuntime()
+ val usedmem = ((rt.totalMemory() - rt.freeMemory()) / 1024) / 1024
+ val totalmem = (rt.totalMemory() / 1024) / 1024
+
+ super.act(delta)
+ c_fps.setText(i18n.FormattedText("debug.c_fps", Gdx.graphics.framesPerSecond.toString()))
+ c_mem.setText(i18n.FormattedText("debug.c_mem", usedmem.toString(), totalmem.toString()))
+ }
+} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/ui/InventoryAnimatedItem.java b/core/src/kz/ilotterytea/maxon/ui/InventoryAnimatedItem.java
new file mode 100644
index 0000000..af6c6da
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/InventoryAnimatedItem.java
@@ -0,0 +1,26 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.scenes.scene2d.ui.*;
+import kz.ilotterytea.maxon.player.MaxonItem;
+
+public class InventoryAnimatedItem extends Stack {
+ public InventoryAnimatedItem(
+ MaxonItem item,
+ Skin skin,
+ Integer amount
+ ) {
+ super(new Image(item.icon.getDrawable()));
+
+ Table table = new Table();
+ table.setSize(super.getWidth(), super.getHeight());
+ table.add(new Label(String.format("x%s", amount), skin, "default")).bottom().right();
+
+ TextTooltip.TextTooltipStyle style = new TextTooltip.TextTooltipStyle();
+ style.label = new Label.LabelStyle();
+ style.label.font = skin.getFont("small");
+ style.label.fontColor = skin.getColor("white");
+
+ super.add(table);
+ super.addListener(new TextTooltip(String.format("%s (%s)", item.name, item.multiplier), style));
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/ui/LeafParticle.java b/core/src/kz/ilotterytea/maxon/ui/LeafParticle.java
new file mode 100644
index 0000000..82879cf
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/LeafParticle.java
@@ -0,0 +1,30 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.graphics.g2d.Batch;
+import com.badlogic.gdx.graphics.g2d.Sprite;
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+
+public class LeafParticle extends Sprite {
+ private float angle, x, y, vertAngle, rotation, time;
+
+ public LeafParticle(TextureRegion region, float x, float y, float angle, float vertAngle, float rotation) {
+ super(region);
+ this.angle = angle;
+ this.vertAngle = vertAngle;
+ this.rotation = rotation;
+ this.x = x;
+ this.y = y;
+ }
+
+ @Override
+ public void draw(Batch batch) {
+ this.time = Gdx.graphics.getDeltaTime();
+ this.x -= (float) Math.sin(time) * this.angle;
+ this.y -= (float) Math.sin(time) * this.vertAngle;
+
+ super.setPosition(x, y);
+ super.setRotation(super.getRotation() + this.rotation);
+ super.draw(batch);
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/ui/MovingChessBackground.java b/core/src/kz/ilotterytea/maxon/ui/MovingChessBackground.java
new file mode 100644
index 0000000..a585d0b
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/MovingChessBackground.java
@@ -0,0 +1,150 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.graphics.g2d.SpriteBatch;
+import com.badlogic.gdx.scenes.scene2d.ui.Image;
+import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class MovingChessBackground {
+ private final Logger log = LoggerFactory.getLogger(MovingChessBackground.class.getSimpleName());
+
+ private final float velocityX;
+ private final float velocityY;
+ private float screenWidth, screenHeight;
+ private final List<Drawable> drawables;
+
+ private final ArrayList<ArrayList<Image>> tiles;
+
+ /**
+ * Background that looking like chess and moves.
+ * @param velocityX X Velocity
+ * @param velocityY Y Velocity
+ * @param screenWidth Width of the screen
+ * @param screenHeight Height of the screen
+ * @param drawables Drawables to draw
+ */
+ public MovingChessBackground(
+ float velocityX,
+ float velocityY,
+ float screenWidth,
+ float screenHeight,
+ Drawable... drawables
+ ) {
+ this.tiles = new ArrayList<>();
+ this.drawables = Arrays.asList(drawables);
+
+ this.velocityX = velocityX;
+ this.velocityY = velocityY;
+ this.screenWidth = screenWidth;
+ this.screenHeight = screenHeight;
+
+ update(screenWidth, screenHeight);
+ }
+
+ /**
+ * Clean up the background tiles and create new ones.
+ * @param width Width of the screen.
+ * @param height Height of the screen.
+ */
+ public void update(float width, float height) {
+ screenWidth = width;
+ screenHeight = height;
+
+ log.info("Starting to update the \"Moving chess\" background...");
+ tiles.clear();
+ log.info("List of tiles is cleared!");
+
+ float totalDWidth = 0, totalDHeight = 0;
+
+ for (Drawable drawable : drawables) {
+ totalDWidth += 64;
+ totalDHeight += 64;
+ }
+
+ totalDWidth = totalDWidth / drawables.size();
+ totalDHeight = totalDHeight / drawables.size();
+
+ log.info(String.format("Total size of %s drawables: %sx%s", drawables.size(), totalDWidth, totalDHeight));
+
+ int DIndex = 0;
+
+ log.info("Starting to generating tiles...");
+
+ for (int h = 0; h < height / totalDHeight + 3; h++) {
+ tiles.add(h, new ArrayList<Image>());
+
+ for (int w = -1; w < width / totalDWidth; w++) {
+ if (DIndex + 1 > drawables.size()) DIndex = 0;
+ Image tile = new Image(drawables.get(DIndex++));
+ tile.setSize(64f, 64f);
+
+ tile.setPosition(tile.getWidth() * w, tile.getHeight() * h);
+
+ tiles.get(h).add(tile);
+ }
+ }
+
+ log.info("\"Moving chess\" background is successfully updated!");
+ }
+
+ /**
+ * Draw the background tiles.
+ * @param batch Sprite batch.
+ */
+ public void draw(
+ SpriteBatch batch
+ ) {
+ ArrayList<ArrayList<Image>> outYSprites = new ArrayList<>();
+
+ // For horizontal:
+ for (ArrayList<Image> array : tiles) {
+ for (Image tile : array) {
+ tile.setPosition(tile.getX() + velocityX, tile.getY() + velocityY);
+ tile.draw(batch, 1f);
+
+ if (tile.getX() > screenWidth) {
+ Image fTile = array.get(0);
+
+ tile.setPosition(fTile.getX() - tile.getWidth(), fTile.getY());
+
+ if (array.size() > 1 && tile.getDrawable() == fTile.getDrawable()) {
+ tile.setDrawable(array.get(1).getDrawable());
+ }
+
+ array.remove(tile);
+ array.add(0, tile);
+ }
+
+ if (!outYSprites.contains(array) && tile.getY() > screenHeight) {
+ outYSprites.add(array);
+ }
+ }
+ }
+
+ // For vertical:
+ for (ArrayList<Image> array : outYSprites) {
+ int index = 0;
+
+ for (Image tile : array) {
+ if (index + 1 > tiles.get(0).size()) index = 0;
+ Image fTile = tiles.get(0).get(index++);
+ tile.setPosition(tile.getX(), fTile.getY() - tile.getHeight());
+
+ if (fTile.getDrawable() == tile.getDrawable()) {
+ if (index + 1 > tiles.get(0).size()) index = 0;
+ tile.setDrawable(tiles.get(0).get(index).getDrawable());
+ }
+ }
+
+ tiles.remove(array);
+ tiles.add(0, array);
+ }
+
+ outYSprites.clear();
+ }
+} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/ui/NinepatchButton.java b/core/src/kz/ilotterytea/maxon/ui/NinepatchButton.java
new file mode 100644
index 0000000..ab96c63
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/NinepatchButton.java
@@ -0,0 +1,28 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.graphics.g2d.NinePatch;
+import com.badlogic.gdx.scenes.scene2d.ui.Skin;
+import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
+import com.badlogic.gdx.scenes.scene2d.utils.NinePatchDrawable;
+
+public class NinepatchButton extends TextButton {
+ public NinepatchButton(
+ NinePatch up,
+ NinePatch down,
+ NinePatch over,
+ String text,
+ Skin skin,
+ String styleName
+ ) {
+ super(text, skin, styleName);
+ TextButtonStyle style = new TextButtonStyle();
+
+ style.up = new NinePatchDrawable(up);
+ style.down = new NinePatchDrawable(down);
+ style.over = new NinePatchDrawable(over);
+ style.fontColor = skin.getColor("white");
+ style.font = skin.getFont("default");
+
+ super.setStyle(style);
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/ui/OptionsTable.java b/core/src/kz/ilotterytea/maxon/ui/OptionsTable.java
new file mode 100644
index 0000000..2b0a185
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/OptionsTable.java
@@ -0,0 +1,180 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.audio.Music;
+import com.badlogic.gdx.files.FileHandle;
+import com.badlogic.gdx.math.Interpolation;
+import com.badlogic.gdx.scenes.scene2d.InputEvent;
+import com.badlogic.gdx.scenes.scene2d.actions.Actions;
+import com.badlogic.gdx.scenes.scene2d.ui.*;
+import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
+import com.badlogic.gdx.utils.Align;
+import kz.ilotterytea.maxon.MaxonConstants;
+import kz.ilotterytea.maxon.MaxonGame;
+import kz.ilotterytea.maxon.screens.SplashScreen;
+import kz.ilotterytea.maxon.utils.I18N;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+public class OptionsTable extends Table {
+ public OptionsTable(
+ final MaxonGame game,
+ Skin skin,
+ Skin widgetSkin,
+ final Music music,
+ final Table menuTable,
+ final Image bgImage,
+ final Image brandLogo
+ ) {
+ super();
+
+ Label optionsLabel = new Label(game.locale.TranslatableText("options.title"), skin);
+ optionsLabel.setAlignment(Align.center);
+ super.add(optionsLabel).fillX().pad(81f).row();
+
+ Table lidlOptionsTable = new Table();
+
+ // Music button:
+ final TextButton musicButton = new TextButton(game.locale.FormattedText("options.music", (game.prefs.getBoolean("music", true)) ? "ON" : "OFF"), widgetSkin, "default");
+
+ musicButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ game.prefs.putBoolean("music", !game.prefs.getBoolean("music", true));
+ game.prefs.flush();
+
+ if (game.prefs.getBoolean("music", true)) {
+ music.setVolume(1f);
+ music.setLooping(true);
+ music.play();
+ } else {
+ music.stop();
+ }
+
+ musicButton.setText(game.locale.FormattedText("options.music", (game.prefs.getBoolean("music", true)) ? "ON" : "OFF"));
+ }
+ });
+
+ lidlOptionsTable.add(musicButton).size(512f, 81f).pad(10f).left();
+
+ final TextButton soundButton = new TextButton(game.locale.FormattedText("options.sound", (game.prefs.getBoolean("sound", true)) ? "ON" : "OFF"), widgetSkin, "default");
+
+ soundButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ game.prefs.putBoolean("sound", !game.prefs.getBoolean("sound", true));
+ game.prefs.flush();
+
+ soundButton.setText(game.locale.FormattedText("options.sound", (game.prefs.getBoolean("sound", true)) ? "ON" : "OFF"));
+ }
+ });
+
+ lidlOptionsTable.add(soundButton).size(512f, 81f).pad(10f).right().row();
+
+ final TextButton vsyncButton = new TextButton(game.locale.FormattedText("options.vsync", (game.prefs.getBoolean("vsync", true)) ? "ON" : "OFF"), widgetSkin, "default");
+
+ vsyncButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ game.prefs.putBoolean("vsync", !game.prefs.getBoolean("vsync", true));
+ game.prefs.flush();
+
+ if (game.prefs.getBoolean("vsync", true)) {
+ Gdx.graphics.setVSync(true);
+ } else {
+ Gdx.graphics.setVSync(false);
+ }
+
+ vsyncButton.setText(game.locale.FormattedText("options.vsync", (game.prefs.getBoolean("vsync", true)) ? "ON" : "OFF"));
+ }
+ });
+
+ lidlOptionsTable.add(vsyncButton).size(512f, 81f).pad(10f).left();
+
+ final TextButton fullscreenButton = new TextButton(game.locale.FormattedText("options.fullscreen", (game.prefs.getBoolean("fullscreen", false)) ? "ON" : "OFF"), widgetSkin, "default");
+
+ fullscreenButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ game.prefs.putBoolean("fullscreen", !game.prefs.getBoolean("fullscreen", false));
+ game.prefs.flush();
+
+ if (game.prefs.getBoolean("fullscreen", false)) {
+ Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
+ } else {
+ Gdx.graphics.setWindowedMode(game.prefs.getInteger("width", Gdx.graphics.getWidth()), game.prefs.getInteger("height", Gdx.graphics.getHeight()));
+ }
+
+ fullscreenButton.setText(game.locale.FormattedText("options.fullscreen", (game.prefs.getBoolean("fullscreen", false)) ? "ON" : "OFF"));
+ }
+ });
+
+ lidlOptionsTable.add(fullscreenButton).size(512f, 81f).pad(10f).right().row();
+
+ super.add(lidlOptionsTable).center().row();
+
+ String[] fh4Locale = game.locale.getFileHandle().nameWithoutExtension().split("_");
+ Locale locale = new Locale(fh4Locale[0], fh4Locale[1]);
+
+ final TextButton switchLangButton = new TextButton(game.locale.FormattedText("options.language", locale.getDisplayLanguage(), locale.getDisplayCountry()), widgetSkin, "default");
+
+ switchLangButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ int index = 0;
+ ArrayList<FileHandle> fhArray = new ArrayList<>();
+ fhArray.add(MaxonConstants.FILE_RU_RU);
+ fhArray.add(MaxonConstants.FILE_EN_US);
+
+ if (fhArray.indexOf(game.locale.getFileHandle()) + 1 < fhArray.size()) {
+ index = fhArray.indexOf(game.locale.getFileHandle()) + 1;
+ }
+
+ FileHandle fhNext = fhArray.get(index);
+
+ game.locale = new I18N(fhNext);
+ game.prefs.putString("lang", fhNext.nameWithoutExtension());
+ game.prefs.flush();
+
+ String[] fh4Locale = fhNext.nameWithoutExtension().split("_");
+ Locale locale = new Locale(fh4Locale[0], fh4Locale[1]);
+
+ switchLangButton.setText(game.locale.FormattedText("options.language", locale.getDisplayLanguage(), locale.getDisplayCountry()));
+ game.setScreen(new SplashScreen(game));
+ music.stop();
+ }
+ });
+
+ super.add(switchLangButton).size(1024f, 81f).padTop(91f).center().row();
+
+ final TextButton optionsCloseButton = new TextButton(game.locale.TranslatableText("options.close"), widgetSkin, "default");
+
+ optionsCloseButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ close(menuTable, bgImage, brandLogo);
+ }
+ });
+
+ super.add(optionsCloseButton).size(1024f, 81f).pad(91f).center().row();
+
+ super.setPosition(0, 0);
+ super.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
+ }
+
+ private void close(Table menu, Image bg, Image logo) {
+ super.clearActions();
+ super.addAction(Actions.moveTo(Gdx.graphics.getWidth(), super.getY(), 0.75f, Interpolation.sine));
+
+ menu.clearActions();
+ menu.addAction(Actions.moveTo(0, menu.getY(), 0.75f, Interpolation.sine));
+
+ bg.clearActions();
+ bg.addAction(Actions.alpha(0.25f));
+
+ logo.addAction(
+ Actions.moveTo(logo.getX(), logo.getY() - 512f, 0.5f, Interpolation.sine)
+ );
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/ui/PurchaseItem.java b/core/src/kz/ilotterytea/maxon/ui/PurchaseItem.java
new file mode 100644
index 0000000..5ef7ab6
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/PurchaseItem.java
@@ -0,0 +1,85 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.scenes.scene2d.Actor;
+import com.badlogic.gdx.scenes.scene2d.InputEvent;
+import com.badlogic.gdx.scenes.scene2d.ui.*;
+import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
+import com.badlogic.gdx.utils.Align;
+import kz.ilotterytea.maxon.MaxonConstants;
+import kz.ilotterytea.maxon.player.MaxonItem;
+
+public class PurchaseItem extends Table {
+ private double price;
+ private final Label priceLabel;
+ private final MaxonItem item;
+
+ private boolean isDisabled = false;
+
+ public PurchaseItem(
+ Skin skin,
+ MaxonItem item
+ ) {
+ super(skin);
+ super.setBackground("shop_item");
+ super.align(Align.left | Align.center);
+
+ super.add(item.icon).size(64f).pad(6f);
+
+ this.price = item.price;
+ this.item = item;
+
+ Table summary = new Table();
+ summary.align(Align.topLeft);
+
+ Label name = new Label(item.name, skin, "item_title");
+ name.setAlignment(Align.left);
+
+ this.priceLabel = new Label(String.format("%s SQP (%s/click)", MaxonConstants.DECIMAL_FORMAT.format(price), MaxonConstants.DECIMAL_FORMAT.format(item.multiplier)), skin, "item_price");
+ this.priceLabel.setAlignment(Align.left);
+
+ summary.add(name).align(Align.left).row();
+ summary.add(this.priceLabel).grow();
+
+ super.add(summary).grow();
+
+ super.addListener(new ClickListener() {
+ @Override
+ public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) {
+ super.enter(event, x, y, pointer, fromActor);
+ if (!isDisabled) {
+ PurchaseItem.super.setBackground("shop_item_hover");
+ }
+ }
+ @Override
+ public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
+ super.exit(event, x, y, pointer, toActor);
+ if (!isDisabled) {
+ PurchaseItem.super.setBackground("shop_item");
+ }
+ }
+ });
+ }
+
+ public void setPrice(double price) {
+ this.price = price;
+ this.priceLabel.setText(String.format("%s SQP (%s/click)", MaxonConstants.DECIMAL_FORMAT.format(price), MaxonConstants.DECIMAL_FORMAT.format(item.multiplier)));
+ }
+
+ public double getPrice() {
+ return price;
+ }
+
+ public boolean isDisabled() {
+ return isDisabled;
+ }
+
+ public void setDisabled(boolean disabled) {
+ isDisabled = disabled;
+
+ super.setBackground(isDisabled ? "bg" : "shop_item");
+ }
+
+ public MaxonItem getItem() {
+ return item;
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/ui/SavegameWidget.java b/core/src/kz/ilotterytea/maxon/ui/SavegameWidget.java
new file mode 100644
index 0000000..7069911
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/SavegameWidget.java
@@ -0,0 +1,202 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.graphics.g2d.TextureAtlas;
+import com.badlogic.gdx.scenes.scene2d.Action;
+import com.badlogic.gdx.scenes.scene2d.InputEvent;
+import com.badlogic.gdx.scenes.scene2d.Stage;
+import com.badlogic.gdx.scenes.scene2d.actions.Actions;
+import com.badlogic.gdx.scenes.scene2d.ui.*;
+import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
+import com.badlogic.gdx.utils.Align;
+import com.badlogic.gdx.utils.Disposable;
+import kz.ilotterytea.maxon.MaxonGame;
+import kz.ilotterytea.maxon.player.MaxonSavegame;
+import kz.ilotterytea.maxon.screens.GameScreen;
+import kz.ilotterytea.maxon.utils.formatters.NumberFormatter;
+
+import java.io.IOException;
+
+public class SavegameWidget extends Table implements Disposable {
+ private final Skin skin;
+ private MaxonSavegame savegame;
+ private final Table dataTable, controlTable;
+ private final TextureAtlas atlas;
+ private final MaxonGame game;
+ private final Stage stage;
+
+ public SavegameWidget(final MaxonGame game, Skin skin, final Stage stage, final MaxonSavegame savegame) {
+ super();
+ this.game = game;
+ this.stage = stage;
+ this.atlas = game.assetManager.get("MainSpritesheet.atlas", TextureAtlas.class);
+
+ this.skin = skin;
+ this.savegame = savegame;
+
+ this.dataTable = new Table(this.skin);
+ this.dataTable.pad(16f);
+ this.dataTable.setBackground("bg");
+
+ super.add(this.dataTable).grow().padBottom(16f).row();
+
+ this.controlTable = new Table();
+ this.controlTable.align(Align.left);
+ super.add(this.controlTable).growX();
+
+ if (savegame == null) {
+ createEmpty();
+ } else {
+ createWithSavegame();
+ }
+ }
+
+ private void createEmpty() {
+ final boolean[] gameCreation = {false};
+
+ // Body
+ Table body = new Table();
+
+ Label name = new Label("New Game", skin);
+ name.setAlignment(Align.center);
+ body.add(name).grow().row();
+
+ this.dataTable.add(body).grow().row();
+
+ // - - - C O N T R O L - - -
+ TextButton playButton = new TextButton("play", skin);
+ TextField field = new TextField(System.getProperty("user.name", "Maxon"), skin);
+
+ body.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+
+ if (!gameCreation[0]) {
+ name.setText("What is your name?");
+
+ body.add(field).growX();
+
+ controlTable.add(playButton).growX();
+ gameCreation[0] = true;
+ }
+ }
+ });
+
+ playButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+
+ if (savegame == null) {
+ savegame = new MaxonSavegame();
+ savegame.name = field.getText();
+ }
+
+ moveToNextScreen();
+ }
+ });
+ }
+
+ private void createWithSavegame() {
+ // - - - S A V E G A M E D A T A - - -
+ // Header
+ Table header = new Table();
+
+ Label name = new Label(savegame.name, skin);
+ header.add(name).grow();
+
+ long minutes = savegame.elapsedTime / 1000 / 60;
+ long seconds = savegame.elapsedTime / 1000 % 60;
+
+ Label time = new Label(String.format("%s:%s", NumberFormatter.pad(minutes), NumberFormatter.pad(seconds)), skin);
+ time.setAlignment(Align.right);
+ header.add(time).grow().row();
+
+ this.dataTable.add(header).grow().row();
+
+ // Data
+ Table data = new Table();
+ data.align(Align.left);
+
+ // Points
+ Image pointsIcon = new Image(atlas.findRegion("points"));
+ data.add(pointsIcon).size(32f, 32f).padRight(8f);
+
+ Label points = new Label(NumberFormatter.format(savegame.points), skin);
+ data.add(points).padRight(32f);
+
+ // Unit
+ long amount = savegame.inv.size();
+
+ Image unitIcon = new Image(atlas.findRegion("points"));
+ data.add(unitIcon).size(32f, 32f).padRight(8f);
+
+ Label unit = new Label(NumberFormatter.format(amount), skin);
+ data.add(unit).padRight(32f);
+
+ // Multiplier
+ Image multiplierIcon = new Image(atlas.findRegion("multiplier"));
+ data.add(multiplierIcon).size(32f, 32f).padRight(8f);
+
+ Label multiplier = new Label(NumberFormatter.format(savegame.multiplier), skin);
+ data.add(multiplier);
+
+ this.dataTable.add(data).grow();
+
+ // - - - C O N T R O L - - -
+ TextButton playButton = new TextButton(game.locale.TranslatableText("menu.continue"), skin);
+ controlTable.add(playButton).padRight(16f).growX();
+
+ playButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ moveToNextScreen();
+ }
+ });
+
+ TextButton resetButton = new TextButton(game.locale.TranslatableText("menu.reset"), skin);
+ controlTable.add(resetButton);
+
+ resetButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+
+ controlTable.clear();
+ dataTable.clear();
+ createEmpty();
+ }
+ });
+ }
+
+ private void moveToNextScreen() {
+ Image bg = new Image(skin, "white_tile");
+ bg.setFillParent(true);
+
+ bg.addAction(
+ Actions.sequence(
+ Actions.alpha(0.0f),
+ Actions.alpha(1.0f, 1.5f),
+ Actions.delay(0.5f),
+ new Action() {
+ @Override
+ public boolean act(float delta) {
+ try {
+ game.setScreen(new GameScreen(game, savegame, 0));
+ } catch (IOException | ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return true;
+ }
+ }
+ )
+ );
+ stage.addActor(bg);
+ }
+
+ @Override
+ public void dispose() {
+ atlas.dispose();
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/ui/SupaIconButton.java b/core/src/kz/ilotterytea/maxon/ui/SupaIconButton.java
new file mode 100644
index 0000000..2ebdbc0
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/ui/SupaIconButton.java
@@ -0,0 +1,24 @@
+package kz.ilotterytea.maxon.ui;
+
+import com.badlogic.gdx.graphics.g2d.NinePatch;
+import com.badlogic.gdx.scenes.scene2d.ui.*;
+import com.badlogic.gdx.utils.Align;
+
+public class SupaIconButton extends Stack {
+
+ public SupaIconButton(
+ NinePatch ninepatch,
+ CharSequence text,
+ Skin skin
+ ) {
+ super(new Image(ninepatch));
+
+ Label label = new Label(text, skin);
+ Table table = new Table();
+
+ label.setAlignment(Align.center);
+
+ table.add(label).expand().fillX().center().left();
+ super.add(table);
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/utils/AssetLoading.java b/core/src/kz/ilotterytea/maxon/utils/AssetLoading.java
new file mode 100644
index 0000000..7a3e13e
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/utils/AssetLoading.java
@@ -0,0 +1,191 @@
+package kz.ilotterytea.maxon.utils;
+
+import com.badlogic.gdx.assets.AssetManager;
+import com.badlogic.gdx.assets.loaders.SkinLoader;
+import com.badlogic.gdx.audio.Music;
+import com.badlogic.gdx.graphics.Texture;
+import com.badlogic.gdx.graphics.g2d.TextureAtlas;
+import com.badlogic.gdx.scenes.scene2d.ui.Skin;
+import kz.ilotterytea.maxon.anim.SpriteUtils;
+import kz.ilotterytea.maxon.player.MaxonItemEnum;
+import kz.ilotterytea.maxon.player.MaxonItemRegister;
+import kz.ilotterytea.maxon.ui.AnimatedImage;
+import net.mgsx.gltf.loaders.glb.GLBAssetLoader;
+import net.mgsx.gltf.scene3d.scene.SceneAsset;
+
+public class AssetLoading {
+ public static void setup(AssetManager am) {
+ am.setLoader(SceneAsset.class, ".glb", new GLBAssetLoader());
+ }
+
+ public static void queue(AssetManager am) {
+ // Texture atlases:
+ am.load("sprites/env/environment.atlas", TextureAtlas.class);
+ am.load("sprites/gui/brand.atlas", TextureAtlas.class);
+ am.load("sprites/gui/icons.atlas", TextureAtlas.class);
+ am.load("sprites/gui/ilotterytea.atlas", TextureAtlas.class);
+ am.load("sprites/gui/widgets.atlas", TextureAtlas.class);
+ am.load("sprites/gui/widgets.skin", Skin.class, new SkinLoader.SkinParameter("sprites/gui/widgets.atlas"));
+
+ am.load("sprites/gui/widgeticons.atlas", TextureAtlas.class);
+ am.load("sprites/gui/friends.atlas", TextureAtlas.class);
+ am.load("sprites/gui/friends.skin", Skin.class, new SkinLoader.SkinParameter("sprites/gui/friends.atlas"));
+
+ am.load("MainSpritesheet.atlas", TextureAtlas.class);
+ am.load("MainSpritesheet.skin", Skin.class, new SkinLoader.SkinParameter("MainSpritesheet.atlas"));
+
+ // Models:
+ am.load("models/scenes/living_room.glb", SceneAsset.class);
+
+ // Cat item textures:
+ am.load("sprites/sheet/loadingCircle.png", Texture.class);
+ am.load("sprites/sheet/bror.png", Texture.class);
+ am.load("sprites/sheet/manlooshka.png", Texture.class);
+ am.load("sprites/sheet/furios_cat.png", Texture.class);
+ am.load("sprites/sheet/sandwich_cat.png", Texture.class);
+ am.load("sprites/sheet/thirsty_cat.png", Texture.class);
+ am.load("sprites/sheet/tvcat.png", Texture.class);
+ am.load("sprites/sheet/progcat.png", Texture.class);
+ am.load("sprites/sheet/screamcat.png", Texture.class);
+ am.load("sprites/sheet/hellcat.png", Texture.class);
+ am.load("sprites/sheet/lurker.png", Texture.class);
+ am.load("sprites/sheet/piano_cat.png", Texture.class);
+ am.load("sprites/sheet/bee_cat.png", Texture.class);
+ am.load("sprites/sheet/busy.png", Texture.class);
+ am.load("sprites/sheet/aeae.png", Texture.class);
+ am.load("sprites/sheet/succat.png", Texture.class);
+
+ // Music:
+ am.load("mus/menu/mus_menu_intro.ogg", Music.class);
+ am.load("mus/menu/mus_menu_loop.ogg", Music.class);
+ am.load("mus/game/onwards.wav", Music.class);
+ am.load("mus/game/paris.wav", Music.class);
+ am.load("mus/game/adieu.wav", Music.class);
+ am.load("mus/game/shopping_spree.wav", Music.class);
+ // Sounds:
+ }
+
+ public static void registerItems(AssetManager am, I18N i18n) {
+ MaxonItemRegister.clear();
+
+ MaxonItemRegister.register(
+ 0, i18n.TranslatableText("pet.bror.name"), i18n.TranslatableText("pet.bror.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/bror.png", Texture.class), 112, 112, 11, 7)),
+ MaxonItemEnum.SLAVE,
+ 100,
+ 1f
+ );
+
+ MaxonItemRegister.register(
+ 1, i18n.TranslatableText("pet.sandwich.name"), i18n.TranslatableText("pet.sandwich.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/sandwich_cat.png", Texture.class), 112, 112, 4, 7)),
+ MaxonItemEnum.SLAVE,
+ 1000,
+ 12f
+ );
+
+ MaxonItemRegister.register(
+ 2, i18n.TranslatableText("pet.manlooshka.name"), i18n.TranslatableText("pet.manlooshka.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/manlooshka.png", Texture.class), 112, 112, 10, 4)),
+ MaxonItemEnum.SLAVE,
+ 5000,
+ 36f
+ );
+
+ MaxonItemRegister.register(
+ 3, i18n.TranslatableText("pet.thirsty.name"), i18n.TranslatableText("pet.thirsty.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/thirsty_cat.png", Texture.class), 112, 112, 6, 3)),
+ MaxonItemEnum.SLAVE,
+ 10000,
+ 100f
+ );
+
+ MaxonItemRegister.register(
+ 4, i18n.TranslatableText("pet.furios.name"), i18n.TranslatableText("pet.furios.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/furios_cat.png", Texture.class), 112, 112, 7, 4)),
+ MaxonItemEnum.SLAVE,
+ 750000,
+ 320f
+ );
+
+ MaxonItemRegister.register(
+ 5, i18n.TranslatableText("pet.tvcat.name"), i18n.TranslatableText("pet.tvcat.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/tvcat.png", Texture.class), 112, 112, 5, 5)),
+ MaxonItemEnum.SLAVE,
+ 1500000,
+ 950f
+ );
+
+ MaxonItemRegister.register(
+ 6, i18n.TranslatableText("pet.progcat.name"), i18n.TranslatableText("pet.progcat.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/progcat.png", Texture.class), 112, 112, 7, 6)),
+ MaxonItemEnum.SLAVE,
+ 3000000,
+ 2900f
+ );
+
+ MaxonItemRegister.register(
+ 7, i18n.TranslatableText("pet.screamcat.name"), i18n.TranslatableText("pet.screamcat.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/screamcat.png", Texture.class), 112, 112, 10, 10)),
+ MaxonItemEnum.SLAVE,
+ 7000000,
+ 8700f
+ );
+
+ MaxonItemRegister.register(
+ 8, i18n.TranslatableText("pet.hellcat.name"), i18n.TranslatableText("pet.hellcat.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/hellcat.png", Texture.class), 128, 128, 10, 8)),
+ MaxonItemEnum.SLAVE,
+ 13000000,
+ 26100f
+ );
+
+ MaxonItemRegister.register(
+ 9, i18n.TranslatableText("pet.lurker.name"), i18n.TranslatableText("pet.lurker.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/lurker.png", Texture.class), 112, 112, 10, 7)),
+ MaxonItemEnum.SLAVE,
+ 20000000,
+ 78300f
+ );
+
+ MaxonItemRegister.register(
+ 10, i18n.TranslatableText("pet.piano.name"), i18n.TranslatableText("pet.piano.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/piano_cat.png", Texture.class), 128, 128, 5, 3)),
+ MaxonItemEnum.SLAVE,
+ 40000000,
+ 234900f
+ );
+
+ MaxonItemRegister.register(
+ 11, i18n.TranslatableText("pet.bee.name"), i18n.TranslatableText("pet.bee.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/bee_cat.png", Texture.class), 112, 112, 10, 10)),
+ MaxonItemEnum.SLAVE,
+ 70000000,
+ 704700f
+ );
+
+ MaxonItemRegister.register(
+ 12, i18n.TranslatableText("pet.busy.name"), i18n.TranslatableText("pet.busy.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/busy.png", Texture.class), 112, 112, 5, 5)),
+ MaxonItemEnum.SLAVE,
+ 150000000,
+ 2114100f
+ );
+
+ MaxonItemRegister.register(
+ 13, i18n.TranslatableText("pet.aeae.name"), i18n.TranslatableText("pet.aeae.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/aeae.png", Texture.class), 112, 112, 11, 10)),
+ MaxonItemEnum.SLAVE,
+ 250000000,
+ 6342300f
+ );
+
+ MaxonItemRegister.register(
+ 14, i18n.TranslatableText("pet.succat.name"), i18n.TranslatableText("pet.succat.desc"),
+ new AnimatedImage(SpriteUtils.splitToTextureRegions(am.get("sprites/sheet/succat.png", Texture.class), 128, 128, 13, 10)),
+ MaxonItemEnum.SLAVE,
+ 500000000,
+ 19026900f
+ );
+ }
+} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/utils/GameUpdater.java b/core/src/kz/ilotterytea/maxon/utils/GameUpdater.java
new file mode 100644
index 0000000..0133d21
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/utils/GameUpdater.java
@@ -0,0 +1,127 @@
+package kz.ilotterytea.maxon.utils;
+
+import com.google.gson.Gson;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Objects;
+
+
+public class GameUpdater {
+ static class Release {
+ String url;
+ String html_url;
+ String assets_url;
+ String upload_url;
+ String tarball_url;
+ String zipball_url;
+ String discussion_url;
+ int id;
+ String node_id;
+ String tag_name;
+ String target_commitish;
+ String name;
+ String body;
+ boolean draft;
+ boolean prerelease;
+ String created_at;
+ String published_at;
+ Author author;
+ Assets[] assets;
+ }
+
+ static class Author {
+ String login;
+ int id;
+ String node_id;
+ String avatar_url;
+ String gravatar_id;
+ String url;
+ String html_url;
+ String followers_url;
+ String following_url;
+ String gists_url;
+ String starred_url;
+ String subscriptions_url;
+ String organizations_url;
+ String repos_url;
+ String events_url;
+ String received_events_url;
+ String type;
+ boolean site_admin;
+ }
+
+ static class Assets {
+ String url;
+ String browser_download_url;
+ int id;
+ String node_id;
+ String name;
+ String label;
+ String state;
+ String content_type;
+ int size;
+ int download_count;
+ String created_at;
+ String updated_at;
+ Uploader uploader;
+ }
+
+ static class Uploader {
+ String login;
+ int id;
+ String node_id;
+ String avatar_url;
+ String gravatar_id;
+ String url;
+ String html_url;
+ String followers_url;
+ String following_url;
+ String gists_url;
+ String starred_url;
+ String subscriptions_url;
+ String organizations_url;
+ String repos_url;
+ String events_url;
+ String received_events_url;
+ String type;
+ boolean site_admin;
+ }
+
+ private static String readUrl(String urlString) throws Exception {
+ BufferedReader reader = null;
+ try {
+ URL url = new URL(urlString);
+ reader = new BufferedReader(new InputStreamReader(url.openStream()));
+ StringBuffer buffer = new StringBuffer();
+ int read;
+ char[] chars = new char[1024];
+ while ((read = reader.read(chars)) != -1)
+ buffer.append(chars, 0, read);
+
+ return buffer.toString();
+ } finally {
+ if (reader != null)
+ reader.close();
+ }
+ }
+
+ public static boolean isLatestRelease(String githubTag) throws Exception {
+ return Objects.equals(githubTag, getLatestRelease().tag_name);
+ }
+
+ public static String getLatestVersion() throws Exception {
+ return getLatestRelease().tag_name;
+ }
+
+ public static Release getLatestRelease() throws Exception {
+ String url_link = "https://api.github.com/repos/NotDankEnough/MaxonPettingSim/releases/latest";
+
+ String json = readUrl(url_link);
+
+ Gson gson = new Gson();
+
+ return gson.fromJson(json, Release.class);
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/utils/I18N.java b/core/src/kz/ilotterytea/maxon/utils/I18N.java
new file mode 100644
index 0000000..7d4013e
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/utils/I18N.java
@@ -0,0 +1,53 @@
+package kz.ilotterytea.maxon.utils;
+
+import com.badlogic.gdx.files.FileHandle;
+import com.badlogic.gdx.utils.JsonReader;
+import com.badlogic.gdx.utils.JsonValue;
+
+import java.lang.StringBuilder;
+import java.util.*;
+
+public class I18N {
+ private Map<String, String> language = new HashMap<>();
+ private FileHandle fileHandle;
+
+ public I18N(FileHandle fh) {
+ fileHandle = fh;
+
+ JsonValue json = new JsonReader().parse(fileHandle);
+
+ for (JsonValue val : json.iterator()) {
+ this.language.put(val.name, json.getString(val.name));
+ }
+ }
+
+ public FileHandle getFileHandle() { return fileHandle; }
+ public Map<String, String> getLanguage() { return language; }
+
+ public String TranslatableText(String id) {
+ if (language.containsKey(id)) {
+ return language.get(id);
+ }
+ return null;
+ }
+
+ public String FormattedText(String id, CharSequence... params) {
+ if (!language.containsKey(id)) { return null; }
+ Scanner scan = new Scanner(language.get(id));
+ StringBuilder result = new StringBuilder();
+ int index = 0;
+
+ while (scan.hasNext()) {
+ String next = scan.next();
+
+ if (next.contains("%s")) {
+ next = next.replace("%s", params[index]);
+ if (index + 1 < params.length) { index++; }
+ }
+
+ result.append(next).append(' ');
+ }
+
+ return result.toString();
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/utils/OsUtils.java b/core/src/kz/ilotterytea/maxon/utils/OsUtils.java
new file mode 100644
index 0000000..acafb91
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/utils/OsUtils.java
@@ -0,0 +1,109 @@
+package kz.ilotterytea.maxon.utils;
+
+public class OsUtils {
+ private static String OS = System.getProperty("os.name").toLowerCase();
+
+ static public boolean isAndroid = System.getProperty("java.runtime.name").contains("Android");
+ static public boolean isMac = !isAndroid && OS.contains("mac");
+ static public boolean isWindows = !isAndroid && OS.contains("windows");
+ static public boolean isLinux = !isAndroid && OS.contains("linux");
+ static public boolean isIos = !isAndroid && (!(isWindows || isLinux || isMac)) || OS.startsWith("ios");
+
+ static public boolean isARM = System.getProperty("os.arch").startsWith("arm") || System.getProperty("os.arch").startsWith("aarch64");
+ static public boolean is64Bit = System.getProperty("os.arch").contains("64") || System.getProperty("os.arch").startsWith("armv8");
+
+ public static boolean isGwt = false;
+
+ static {
+ try {
+ Class.forName("com.google.gwt.core.client.GWT");
+ isGwt = true;
+ }
+ catch(Exception ignored) { /* IGNORED */ }
+
+ boolean isMOEiOS = "iOS".equals(System.getProperty("moe.platform.name"));
+ if (isMOEiOS || (!isAndroid && !isWindows && !isLinux && !isMac)) {
+ isIos = true;
+ isAndroid = false;
+ isWindows = false;
+ isLinux = false;
+ isMac = false;
+ is64Bit = false;
+ }
+ }
+
+ public static String getUserConfigDirectory()
+ {
+ return getUserConfigDirectory(null);
+ }
+
+ public static String getUserConfigDirectory(String applicationName)
+ {
+ String CONFIG_HOME = null;
+
+ if((CONFIG_HOME = System.getenv("XDG_CONFIG_HOME"))==null)
+ {
+ if(isLinux || isAndroid)
+ {
+ CONFIG_HOME = System.getProperty("user.home")+"/.config";
+ }
+ else if(isMac)
+ {
+ CONFIG_HOME = System.getProperty("user.home")+"/Library/Preferences";
+ }
+ else if(isIos)
+ {
+ CONFIG_HOME = System.getProperty("user.home")+"/Documents";
+ }
+ else if(isWindows)
+ {
+ if((CONFIG_HOME = System.getenv("APPDATA"))==null)
+ {
+ CONFIG_HOME = System.getProperty("user.home")+"/Documents/My Games";
+ }
+ }
+ }
+
+ if(applicationName==null || CONFIG_HOME==null) return CONFIG_HOME;
+
+ return CONFIG_HOME+"/"+applicationName;
+ }
+
+ public static String getUserDataDirectory()
+ {
+ return getUserDataDirectory(null);
+ }
+
+ public static String getUserDataDirectory(String applicationName)
+ {
+ String DATA_HOME = null;
+
+ if((DATA_HOME = System.getenv("XDG_DATA_HOME"))==null)
+ {
+ if(isLinux || isAndroid)
+ {
+ DATA_HOME = System.getProperty("user.home")+"/.local/share";
+ }
+ else if(isMac)
+ {
+ DATA_HOME = System.getProperty("user.home")+"/Library/Application Support";
+ }
+ else if(isIos)
+ {
+ DATA_HOME = System.getProperty("user.home")+"/Documents";
+ }
+ else if(isWindows)
+ {
+ if((DATA_HOME = System.getenv("APPDATA"))==null)
+ {
+ DATA_HOME = System.getProperty("user.home")+"/Documents/My Games";
+ }
+ }
+ }
+
+ if(applicationName==null || DATA_HOME==null) return DATA_HOME;
+
+ return DATA_HOME+"/"+applicationName;
+ }
+
+} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/utils/ScreenshotFactory.java b/core/src/kz/ilotterytea/maxon/utils/ScreenshotFactory.java
new file mode 100644
index 0000000..76cd92b
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/utils/ScreenshotFactory.java
@@ -0,0 +1,51 @@
+package kz.ilotterytea.maxon.utils;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.files.FileHandle;
+import com.badlogic.gdx.graphics.Pixmap;
+import com.badlogic.gdx.graphics.PixmapIO;
+import kz.ilotterytea.maxon.MaxonConstants;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.zip.Deflater;
+
+public class ScreenshotFactory {
+ /**
+ * Take a screenshot.
+ * Default without any compression. Y is flipped.
+ */
+ public static void takeScreenshot(){
+ _takeScreenshot(Deflater.NO_COMPRESSION, true);
+ }
+
+ /**
+ * Take a screenshot. It will be saved in the user data directory, it is different for each platform (Windows: %appdata%/.maxoning/screenshots/, Linux: ~/.local/share/maxoning/screenshots).
+ */
+ public static void takeScreenshot(int compression, boolean flipY){
+ _takeScreenshot(compression, flipY);
+ }
+
+ private static void _takeScreenshot(int compression, boolean flipY) {
+ File file = new File(MaxonConstants.GAME_SCREENSHOT_FOLDER);
+
+ if (!file.exists()) {
+ file.mkdirs();
+ }
+
+ DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy_MM_dd-HHmmss");
+ LocalDateTime now = LocalDateTime.now();
+ Pixmap pixmap = Pixmap.createFromFrameBuffer(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight());
+ ByteBuffer pixels = pixmap.getPixels();
+
+ int size = Gdx.graphics.getBackBufferWidth() * Gdx.graphics.getBackBufferHeight() * 4;
+ for (int i = 3; i < size; i += 4) {
+ pixels.put(i, (byte) 255);
+ }
+
+ PixmapIO.writePNG(new FileHandle(file.getPath() + String.format("/screenshot-%s.png", dtf.format(now))), pixmap, compression, flipY);
+ pixmap.dispose();
+ }
+} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/utils/colors/HexToARGB.java b/core/src/kz/ilotterytea/maxon/utils/colors/HexToARGB.java
new file mode 100644
index 0000000..de613eb
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/utils/colors/HexToARGB.java
@@ -0,0 +1,14 @@
+package kz.ilotterytea.maxon.utils.colors;
+
+import com.badlogic.gdx.graphics.Color;
+
+public final class HexToARGB {
+ public static Color convert(String hex) {
+ hex = hex.charAt(0) == '#' ? hex.substring(1) : hex;
+ int r = Integer.valueOf(hex.substring(0, 2), 16);
+ int g = Integer.valueOf(hex.substring(2, 4), 16);
+ int b = Integer.valueOf(hex.substring(4, 6), 16);
+ int a = hex.length() != 8 ? 255 : Integer.valueOf(hex.substring(6, 8), 16);
+ return new Color(r / 255f, g / 255f, b / 255f, a / 255f);
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/utils/formatters/NumberFormatter.java b/core/src/kz/ilotterytea/maxon/utils/formatters/NumberFormatter.java
new file mode 100644
index 0000000..812b79c
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/utils/formatters/NumberFormatter.java
@@ -0,0 +1,36 @@
+package kz.ilotterytea.maxon.utils.formatters;
+
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+public class NumberFormatter {
+ private static final NavigableMap<Long, String> suffixes = new TreeMap<>();
+ static {
+ suffixes.put(1_000L, "k");
+ suffixes.put(1_000_000L, "M");
+ suffixes.put(1_000_000_000L, "G");
+ suffixes.put(1_000_000_000_000L, "T");
+ suffixes.put(1_000_000_000_000_000L, "P");
+ suffixes.put(1_000_000_000_000_000_000L, "E");
+ }
+
+ public static String format(long value) {
+ //Long.MIN_VALUE == -Long.MIN_VALUE so we need an adjustment here
+ if (value == Long.MIN_VALUE) return format(Long.MIN_VALUE + 1);
+ if (value < 0) return "-" + format(-value);
+ if (value < 1000) return Long.toString(value); //deal with easy case
+
+ Map.Entry<Long, String> e = suffixes.floorEntry(value);
+ Long divideBy = e.getKey();
+ String suffix = e.getValue();
+
+ long truncated = value / (divideBy / 10); //the number part of the output times 10
+ boolean hasDecimal = truncated < 100 && (truncated / 10d) != (truncated / 10);
+ return hasDecimal ? (truncated / 10d) + suffix : (truncated / 10) + suffix;
+ }
+
+ public static String pad(long value) {
+ return value <= 9 && value >= 0 ? "0" + value : String.valueOf(value);
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/utils/math/Math.java b/core/src/kz/ilotterytea/maxon/utils/math/Math.java
new file mode 100644
index 0000000..ba1904f
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/utils/math/Math.java
@@ -0,0 +1,17 @@
+package kz.ilotterytea.maxon.utils.math;
+
+public class Math {
+ /**
+ * Get random number from min value to max value
+ * @param min Minimal value
+ * @param max Maximum value
+ * @return Random number between minimal and maximum values
+ */
+ public static int getRandomNumber(int min, int max) {
+ return (int) ((java.lang.Math.random() * (max - min)) + min);
+ }
+
+ public static float percentFromValue(float percentage, int value) {
+ return percentage / 100f * value;
+ }
+}
diff --git a/core/src/kz/ilotterytea/maxon/utils/serialization/GameDataSystem.java b/core/src/kz/ilotterytea/maxon/utils/serialization/GameDataSystem.java
new file mode 100644
index 0000000..4762340
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/utils/serialization/GameDataSystem.java
@@ -0,0 +1,120 @@
+package kz.ilotterytea.maxon.utils.serialization;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.utils.Null;
+import com.google.gson.Gson;
+import kz.ilotterytea.maxon.MaxonConstants;
+import kz.ilotterytea.maxon.player.MaxonSavegame;
+import kz.ilotterytea.maxon.utils.OsUtils;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.util.ArrayList;
+
+/**
+ * External game data system control.
+ * @author NotDankEnough
+ * @since Alpha 1.0
+ */
+public class GameDataSystem {
+ private static final File dir = new File(MaxonConstants.GAME_SAVEGAME_FOLDER);
+ private static final Gson gson = new Gson();
+ private static final Logger log = LoggerFactory.getLogger(GameDataSystem.class.getSimpleName());
+
+ /**
+ * Get all savefiles from savegame directory (/.Maxoning/savegames/)
+ * @return Array of MaxonSavegames
+ * @see MaxonSavegame
+ */
+ public static ArrayList<MaxonSavegame> getSavegames() {
+ if (!dir.exists()) dir.mkdirs();
+
+ ArrayList<MaxonSavegame> saves = new ArrayList<>();
+ File[] files = dir.listFiles();
+
+ assert files != null;
+ for (File file : files) {
+ try {
+
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+
+ MaxonSavegame sav = gson.fromJson(ois.readUTF(), MaxonSavegame.class);
+ saves.add(sav);
+
+ ois.close();
+ fis.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return saves;
+ }
+
+ /**
+ * Convert <b>MaxonSavegame</b> class to <b>JSON</b> string and write in UTF-8 encoding (I'm sorry, encryption enjoyers).
+ * @param savegame Save game object.
+ * @param file_name File name.
+ * @see MaxonSavegame
+ */
+ public static void save(@NotNull MaxonSavegame savegame, @NotNull String file_name) {
+ if (!dir.exists()) dir.mkdirs();
+
+ try {
+ log.info("Saving the game...");
+ FileOutputStream fos = new FileOutputStream(String.format("%s/%s", (OsUtils.isAndroid || OsUtils.isIos) ? Gdx.files.getExternalStoragePath() : dir.getAbsolutePath(), file_name));
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+
+ oos.writeUTF(gson.toJson(savegame));
+ oos.close();
+ log.info(String.format("Success! Savegame located at %s/%s", (OsUtils.isAndroid || OsUtils.isIos) ? Gdx.files.getExternalStoragePath() : dir.getAbsolutePath(), file_name));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Reading a <b>JSON</b> string from the file and convert to <b>MaxonSavegame</b> class.
+ * @param file_name File name. If null - it will get the first file by last modified time.
+ * @return Filled <b>MaxonSavegame</b> class
+ * @see MaxonSavegame
+ */
+ public static MaxonSavegame load(@Null String file_name) {
+ MaxonSavegame sav = null;
+
+ if (new File(dir.getAbsolutePath() + "/" + file_name).exists() && !(OsUtils.isAndroid || OsUtils.isIos)) {
+ try {
+ log.info(String.format("Trying to get the savegame at %s/%s...", dir.getAbsolutePath(), file_name));
+ FileInputStream fis = new FileInputStream(String.format("%s/%s", dir.getAbsolutePath(), file_name));
+ ObjectInputStream oos = new ObjectInputStream(fis);
+
+ sav = gson.fromJson(oos.readUTF(), MaxonSavegame.class);
+ oos.close();
+
+ log.info(String.format("Successfully loaded the savegame from %s/%s!", dir.getAbsolutePath(), file_name));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ if ((OsUtils.isAndroid || OsUtils.isIos) && new File(Gdx.files.getExternalStoragePath() + file_name).exists()) {
+ try {
+ log.info(String.format("Trying to get the savegame at %s%s...", Gdx.files.getExternalStoragePath(), file_name));
+ FileInputStream fis = new FileInputStream(String.format("%s%s", Gdx.files.getExternalStoragePath(), file_name));
+ ObjectInputStream oos = new ObjectInputStream(fis);
+
+ sav = gson.fromJson(oos.readUTF(), MaxonSavegame.class);
+ oos.close();
+
+ log.info(String.format("Successfully loaded the savegame from %s%s!", Gdx.files.getExternalStoragePath(), file_name));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return sav;
+ }
+}