summaryrefslogtreecommitdiff
path: root/core/src/kz/ilotterytea/maxon/utils
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/ilotterytea/maxon/utils
parent10e9df6190ddc3f9c8dd7c86482449bec4651e0c (diff)
upd: moved the whole project under kz.ilotterytea.maxon name
Diffstat (limited to 'core/src/kz/ilotterytea/maxon/utils')
-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
9 files changed, 718 insertions, 0 deletions
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;
+ }
+}