diff options
| author | ilotterytea <iltsu@alright.party> | 2024-06-01 00:51:20 +0500 |
|---|---|---|
| committer | ilotterytea <iltsu@alright.party> | 2024-06-01 00:51:20 +0500 |
| commit | e49f8b310d6032c99406baf04b5ec3eba0fd111f (patch) | |
| tree | 5120a1fbbf923da5ee8bc8561ed1545855aa5547 /core/src/kz/ilotterytea/maxon | |
| parent | 10e9df6190ddc3f9c8dd7c86482449bec4651e0c (diff) | |
upd: moved the whole project under kz.ilotterytea.maxon name
Diffstat (limited to 'core/src/kz/ilotterytea/maxon')
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; + } +} |
