├── sprites ├── unit-factory.png ├── hackusated-wall.png ├── hackusated-wall-large.png ├── no │ ├── hackusated-mender.png │ ├── hackusated-mender-top.png │ ├── hackusated-overdrive.png │ └── hackusated-overdrive-top.png └── hackusated-conveyor │ ├── asdfsd.png │ ├── hackusated-conveyor.png │ ├── hackusated-conveyor-0-0.png │ ├── hackusated-conveyor-1-0.png │ ├── hackusated-conveyor-2-0.png │ ├── hackusated-conveyor-3-0.png │ └── hackusated-conveyor-4-0.png ├── mod.hjson ├── scripts ├── features │ ├── v4.js │ ├── v1.js │ ├── v4 │ │ ├── transform.js │ │ ├── worldoptions.js │ │ └── content.js │ ├── features.js │ ├── v3.js │ └── v2.js ├── libs │ └── toast.js ├── main.js └── menu.js └── README.md /sprites/unit-factory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/unit-factory.png -------------------------------------------------------------------------------- /sprites/hackusated-wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/hackusated-wall.png -------------------------------------------------------------------------------- /sprites/hackusated-wall-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/hackusated-wall-large.png -------------------------------------------------------------------------------- /sprites/no/hackusated-mender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/no/hackusated-mender.png -------------------------------------------------------------------------------- /sprites/no/hackusated-mender-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/no/hackusated-mender-top.png -------------------------------------------------------------------------------- /sprites/no/hackusated-overdrive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/no/hackusated-overdrive.png -------------------------------------------------------------------------------- /sprites/hackusated-conveyor/asdfsd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/hackusated-conveyor/asdfsd.png -------------------------------------------------------------------------------- /sprites/no/hackusated-overdrive-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/no/hackusated-overdrive-top.png -------------------------------------------------------------------------------- /sprites/hackusated-conveyor/hackusated-conveyor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/hackusated-conveyor/hackusated-conveyor.png -------------------------------------------------------------------------------- /sprites/hackusated-conveyor/hackusated-conveyor-0-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/hackusated-conveyor/hackusated-conveyor-0-0.png -------------------------------------------------------------------------------- /sprites/hackusated-conveyor/hackusated-conveyor-1-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/hackusated-conveyor/hackusated-conveyor-1-0.png -------------------------------------------------------------------------------- /sprites/hackusated-conveyor/hackusated-conveyor-2-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/hackusated-conveyor/hackusated-conveyor-2-0.png -------------------------------------------------------------------------------- /sprites/hackusated-conveyor/hackusated-conveyor-3-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/hackusated-conveyor/hackusated-conveyor-3-0.png -------------------------------------------------------------------------------- /sprites/hackusated-conveyor/hackusated-conveyor-4-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QmelZ/hackustry/HEAD/sprites/hackusated-conveyor/hackusated-conveyor-4-0.png -------------------------------------------------------------------------------- /mod.hjson: -------------------------------------------------------------------------------- 1 | { 2 | name: "hackustry", 3 | displayName: "Hackustry", 4 | author: "QmelZ", 5 | description: "Hackustry v4", 6 | version: 4, 7 | minGameVersion: 126, 8 | hidden: true 9 | } 10 | -------------------------------------------------------------------------------- /scripts/features/v4.js: -------------------------------------------------------------------------------- 1 | module.exports = (add) => { 2 | 3 | // launch to any sector 4 | add("launch-anywhere", true, t => { 5 | PlanetDialog.debugSelect = t; 6 | Vars.content.sectors().each(e => e.alwaysUnlocked = t); 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /scripts/features/v1.js: -------------------------------------------------------------------------------- 1 | let hconv; 2 | 3 | Events.on(ContentInitEvent, () => { 4 | hconv = extend(Conveyor, "hackusated-conveyor", { 5 | localizedName: "Hackusated Conveyor", 6 | description: "unit railgun", 7 | category: Category.distribution, 8 | buildVisibility: BuildVisibility.hidden, 9 | inEditor: false, 10 | health: 99999, 11 | speed: 99999, 12 | alwaysUnlocked: true 13 | }); 14 | 15 | hconv.init(); 16 | }); 17 | 18 | module.exports = (add) => { 19 | add("hackusated-conveyor", true, t => { 20 | hconv.inEditor = t; 21 | hconv.buildVisibility = t ? BuildVisibility.shown : BuildVisibility.hidden; 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /scripts/libs/toast.js: -------------------------------------------------------------------------------- 1 | module.exports = function(icon, text){ 2 | if(!icon || !text) return; 3 | 4 | let table = new Table(Tex.button); 5 | table.update(() => { 6 | if(!Vars.ui.hudfrag.shown) table.remove(); 7 | }); 8 | table.margin(12); 9 | table.image(icon).pad(3); 10 | table.add(text).wrap().width(280).get().setAlignment(Align.center, Align.center); 11 | table.pack(); 12 | 13 | let container = Core.scene.table(); 14 | Vars.state.isMenu() ? container.top().right().add(table) : container.top().add(table); 15 | container.setTranslation(0, table.getPrefHeight()); 16 | container.actions( 17 | Actions.translateBy(0, -table.getPrefHeight(), 1, Interp.fade), 18 | Actions.delay(2.5), 19 | Actions.run(() => container.actions( 20 | Actions.translateBy(0, table.getPrefHeight(), 1, Interp.fade), 21 | Actions.remove() 22 | )) 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /scripts/features/v4/transform.js: -------------------------------------------------------------------------------- 1 | function become(u){ 2 | if(!(u instanceof UnitType)) return; 3 | 4 | let c = Vars.player.unit(); 5 | Vars.player.unit(u.spawn(c.team, c.x, c.y)); 6 | c.remove(); 7 | } 8 | 9 | function transform(){ 10 | const dialog = new BaseDialog("transform"); 11 | dialog.addCloseButton(); 12 | 13 | dialog.cont.center().pane(p => { 14 | p.defaults().size(210, 64); 15 | 16 | let i = 0; 17 | Vars.content.units().each(e => { 18 | if(e === UnitTypes.block) return; 19 | 20 | p.button(e.localizedName, new TextureRegionDrawable(e.icon(Cicon.medium)), () => { 21 | become(e); 22 | dialog.hide(); 23 | }); 24 | i++; 25 | if(!(i % 2)) p.row(); 26 | }); 27 | 28 | }).growY().width(Vars.mobile ? Core.graphics.getWidth() : Core.graphics.getWidth()/3); 29 | 30 | dialog.show(); 31 | } 32 | 33 | module.exports = (p) => { 34 | p.button("transform", () => { 35 | transform(); 36 | }).self(s => { 37 | s.get().setDisabled(() => { 38 | if(Vars.state.is(GameState.State.menu)) return true; 39 | if(Vars.net.active()) return true; 40 | return false; 41 | }); 42 | }).left().width(210); 43 | }; 44 | -------------------------------------------------------------------------------- /scripts/features/features.js: -------------------------------------------------------------------------------- 1 | const toast = Vars.headless ? () => {} : require(modName + "/libs/toast"); 2 | 3 | // feature functions and enabling/disabling 4 | const features = {}; 5 | let restart = false; 6 | 7 | const util = { 8 | features(){ 9 | return features; 10 | }, 11 | get(name){ 12 | if(!name || typeof name !== "string") return; 13 | 14 | return Core.settings.getBool(name); 15 | }, 16 | runf(name){ 17 | if(!name || typeof name !== "string") return; 18 | 19 | let enabled = !Core.settings.getBool(name); 20 | Core.settings.put(name, enabled); 21 | features[name].func(enabled); 22 | if(!features[name].toggle){ 23 | if(!restart && !enabled){ 24 | restart = true; 25 | if(Vars.headless){ 26 | Log.warn("[red]this feature needs a game restart to be disabled[]"); 27 | }else{ 28 | Core.scene.dialog.hidden(() => { 29 | toast(Icon.warning, "[red]some features need a game restart to be disabled[]"); 30 | }); 31 | } 32 | } 33 | } 34 | } 35 | }; 36 | module.exports = util; 37 | 38 | function add(name, toggle, func){ 39 | if(!name || typeof name !== "string") return; 40 | if(typeof toggle !== "boolean") return; 41 | if(!func || typeof func !== "function") return; 42 | 43 | features[name] = { 44 | func: func, 45 | toggle: toggle 46 | }; 47 | } 48 | 49 | function load(f){ 50 | require(modName + "/features/" + f)(add); 51 | } 52 | 53 | load("v1"); 54 | load("v2"); 55 | load("v3"); 56 | load("v4"); 57 | 58 | 59 | // if the feature is on it should stay on 60 | Events.on(ClientLoadEvent, () => { 61 | let runt = (f) => features[f].func(true); 62 | for(let f in features){ 63 | if(util.get(f)) runt(f); 64 | } 65 | }); 66 | -------------------------------------------------------------------------------- /scripts/features/v3.js: -------------------------------------------------------------------------------- 1 | module.exports = (add) => { 2 | 3 | // reconstructors take no items and instant 4 | add("reconstructors", false, () => { 5 | Vars.content.blocks().each(e => { 6 | if(!(e instanceof Reconstructor)) return; 7 | e.constructTime = 0; 8 | e.consumes.items(); 9 | }); 10 | }); 11 | 12 | // fixes the built in power source to give infinite power 13 | add("power-sources", true, t => { 14 | Blocks.powerSource.powerProduction = t ? Infinity : 1000000 / 60; 15 | }); 16 | 17 | add("cursed-mode", true, t => { 18 | Vars.content.blocks().each(e => { 19 | if(e instanceof CoreBlock) return; 20 | 21 | if(t){ 22 | if(e.size <= 2) return; 23 | e.size -= 2; 24 | }else{ 25 | if(e.size > 14) return; 26 | e.size += 2; 27 | } 28 | }); 29 | }); 30 | 31 | add("op-turrets", false, () => { 32 | Blocks.scorch.name = "Scorch v5"; 33 | Blocks.ripple.name = "Ripplag"; 34 | Blocks.lancer.name = "Lancerdown"; 35 | Blocks.meltdown.name = "Meltlong"; 36 | Blocks.foreshadow.name = "Foreshadop"; 37 | 38 | Vars.content.blocks().each(e => { 39 | if(e.minfo.mod) return; 40 | 41 | if(!(e instanceof Turret)) return; 42 | e.reloadTime = 0; 43 | e.spread = 0; 44 | e.inaccuracy = 0; 45 | e.recoilAmount = 0; 46 | e.restitution = 0; 47 | e.xRand = 0; 48 | e.cooldown = 10; 49 | e.rotateSpeed = 2147483647; 50 | e.targetGround = true; 51 | e.targetAir = true; 52 | 53 | if(!(e instanceof PowerTurret)) return; 54 | e.chargeTime = 144; 55 | e.chargeMaxDelay = 0; 56 | e.shootType.collidesGround = true; 57 | e.shootType.collidesAir = true; 58 | 59 | if(!(e instanceof LaserTurret)) return; 60 | e.range = 999; 61 | e.shootDuration = 999; 62 | e.shootType.length = 999; 63 | }); 64 | }); 65 | }; 66 | -------------------------------------------------------------------------------- /scripts/features/v4/worldoptions.js: -------------------------------------------------------------------------------- 1 | function worldoptions(){ 2 | const dialog = new BaseDialog("world options"); 3 | dialog.addCloseButton(); 4 | 5 | dialog.cont.center().pane(p => { 6 | p.defaults().height(36); 7 | 8 | function check(name, option){ 9 | if(!name || typeof name !== "string") return; 10 | if(!option || typeof option !== "string") return; 11 | if(typeof Vars.state.rules[option] !== "boolean") return; 12 | 13 | p.check(name, Vars.state.rules[option], () => { 14 | Vars.state.rules[option] = !Vars.state.rules[option]; 15 | }).left(); 16 | p.row(); 17 | } 18 | 19 | check("sandbox mode", "infiniteResources"); 20 | check("wave timer", "waveTimer"); 21 | check("waves", "waves"); 22 | check("waves wait for enemies", "waitEnemies"); 23 | check("editor", "editor"); 24 | check("can game over", "canGameOver"); 25 | check("reactor explosions", "reactorExplosions"); 26 | check("schematics", "schematicsAllowed"); 27 | check("fire", "fire"); 28 | check("units require ammo", "unitAmmo"); 29 | check("unit building with logic", "logicUnitBuild"); 30 | check("lighting", "lighting"); 31 | check("enemy lights", "enemyLights"); 32 | check("core incinerates", "coreIncinerates"); 33 | 34 | p.table(cons(t => { 35 | t.label(() => "unit cap:"); 36 | t.field(Vars.state.rules.unitCap, TextField.TextFieldFilter.digitsOnly, s => { 37 | if(s === "") return; 38 | if(parseInt(s) > Integer.MAX_VALUE) return; 39 | 40 | Vars.state.rules.unitCapVariable = parseInt(s) === 0; 41 | Vars.state.rules.unitCap = parseInt(s); 42 | }); 43 | })); 44 | p.row(); 45 | 46 | }).growY().width(Vars.mobile ? Core.graphics.getWidth() : Core.graphics.getWidth()/3); 47 | 48 | dialog.show(); 49 | } 50 | 51 | module.exports = (p) => { 52 | p.button("world options", () => { 53 | worldoptions(); 54 | }).self(s => { 55 | s.get().setDisabled(() => { 56 | if(Vars.state.is(GameState.State.menu)) return true; 57 | if(Vars.net.client()) return true; 58 | return false; 59 | }); 60 | }).left().width(210); 61 | }; 62 | -------------------------------------------------------------------------------- /scripts/features/v2.js: -------------------------------------------------------------------------------- 1 | importPackage(java.lang); 2 | 3 | const wallbuild = { 4 | updateTile(){ 5 | this.super$updateTile(); 6 | this.health = this.maxHealth; 7 | }, 8 | 9 | collision: () => true, 10 | 11 | no(){ 12 | this.dead = false; 13 | this.health = this.maxHealth; 14 | }, 15 | kill(){ 16 | this.no(); 17 | }, 18 | killed(){ 19 | this.no(); 20 | }, 21 | remove(){ 22 | this.no(); 23 | }, 24 | damage(){ 25 | this.no(); 26 | } 27 | }; 28 | 29 | let wall, largewall; 30 | 31 | Events.on(ContentInitEvent, () => { 32 | wall = extend(Wall, "hackusated-wall", { 33 | localizedName: "Hackusated Wall", 34 | category: Category.defense, 35 | buildVisibility: BuildVisibility.hidden, 36 | inEditor: false, 37 | size: 1, 38 | 39 | health: Integer.MAX_VALUE 40 | }); 41 | wall.buildType = () => extend(Wall.WallBuild, wall, wallbuild); 42 | 43 | largewall = extend(Wall, "hackusated-wall-large", { 44 | localizedName: "Large Hackusated Wall", 45 | category: Category.defense, 46 | buildVisibility: BuildVisibility.hidden, 47 | inEditor: false, 48 | size: 2, 49 | 50 | health: Integer.MAX_VALUE 51 | }); 52 | largewall.buildType = () => extend(Wall.WallBuild, largewall, wallbuild); 53 | 54 | wall.init(); 55 | largewall.init(); 56 | }); 57 | 58 | /* 59 | const mender = extend(MendProjector, "hackusated-mender", { 60 | localizedName: "Hackusated Mender", 61 | category: Category.effect, 62 | buildVisibility: BuildVisibility.hidden, 63 | inEditor: false, 64 | size: 2 65 | }); 66 | 67 | const od = extend(OverdriveProjector, "hackusated-overdrive", { 68 | localizedName: "Hackusated Overdrive", 69 | category: Category.effect, 70 | buildVisibility: BuildVisibility.hidden, 71 | inEditor: false, 72 | size: 2 73 | }); 74 | */ 75 | 76 | module.exports = (add) => { 77 | add("hackusated-walls", true, t => { 78 | wall.inEditor = t; 79 | wall.buildVisibility = t ? BuildVisibility.shown : BuildVisibility.hidden; 80 | largewall.inEditor = t; 81 | largewall.buildVisibility = t ? BuildVisibility.shown : BuildVisibility.hidden; 82 | }); 83 | 84 | /* 85 | add("hackusated-mender", true, t => { 86 | mender.inEditor = t; 87 | mender.buildVisibility = t ? BuildVisibility.shown : BuildVisibility.hidden; 88 | }); 89 | 90 | add("hackusated-overdrive", true, t => { 91 | od.inEditor = t; 92 | od.buildVisibility = t ? BuildVisibility.shown : BuildVisibility.hidden; 93 | }); 94 | */ 95 | }; 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hackustry 2 | First Mindustry cheat mod ever made. 3 | 4 | ### Some clarifications 5 | At this point this mod isn't really a cheat mod anymore. it has became a large testing and utility mod (with a messy codebase). you still can cheat using it, and cheating isn't always bad, and it was known as *the* cheat mod before which is the reason i'm labeling it "cheat mod" here. also i'll port this mod to [groovy](https://groovy-lang.org/) because ~~javascript will be getting limited again in mindustry 7.0.~~ i feel like it. (also js got unlimited again) 6 | 7 | ## Features 8 | ### Finished 9 | - Hackusated Conveyors (ported from v1) 10 | - Hackusated Walls (ported from v2) 11 | - OP Turrets (from v3) 12 | - Removed reconstructor costs (from v3) 13 | - Cursed Mode (v3) 14 | - Power Source Fix (v3) 15 | - Launch Anywhere 16 | - Discord Rich Presence 17 | - Informative Title Bar 18 | - Content Menu 19 | * Change build visibility of blocks (from v3) 20 | * Lock/Unlock/Temporarily Unlock content (from v3) 21 | * Change some stats 22 | - World Options Menu 23 | * Change some common world options (e.g infiniteResources, editor, canGameOver...) 24 | * Change unit cap (improved from v3) 25 | - Transform Menu 26 | * Transform into any unit you want 27 | - New Configuration UI 28 | * Hackustry settings button that opens the UI where everything is controlled from 29 | * Features stay upon game restarts 30 | * Import/Export configuration 31 | ### In Progress/Not Happening 32 | - Account System 33 | * Save names and UUID to quickly swap between 34 | * Possibly save unlocks/saves/features 35 | * In progress (probably will be in the groovy version) 36 | - Unit Factory 37 | * no, already added in v7 38 | - Port to Groovy 39 | * in progress (definitely happening) 40 | * will improve code quality 41 | * will also fix the code being too messy 42 | * groovy closures are amazing 43 | - Add Bundle Support 44 | * translatable cheats wow 45 | * i mean it would help 46 | * most likely with the groovy version 47 | 48 | ## Credits 49 | * Some people who helped me with the code while making the large v4 update: 50 | - [GlennFolker](https://github.com/GlennFolker/) 51 | - [sk7725](https://github.com/sk7725) 52 | - [catsz](https://github.com/catsz/) 53 | * Idea contributors/inspiration for features 54 | - Some inspiration from [sk7725](https://github.com/sk7725) 55 | - Stat Editor Menu idea by [NiChrosia](https://github.com/NiChrosia/) 56 | - Launch Anywhere idea by [ThatOneBepis](https://github.com/ThatOneBepis/) 57 | - Transform Menu idea by [Prosta4okua](https://github.com/Prosta4okua) 58 | - Some miscellaneous ideas by [AnOreo1](https://github.com/AnOreo1/) 59 | - I forgot what they are but [Sh1penfire](https://github.com/Sh1penfire/) also suggested some stuff 60 | -------------------------------------------------------------------------------- /scripts/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | welcome to the start of my spaghetti code 3 | have fun trying to read it without dying 4 | */ 5 | 6 | Vars.enableConsole = true; 7 | 8 | if(Vars.headless){ 9 | throw "no server support yet"; 10 | }else{ 11 | if(Core.app.isDesktop()){ 12 | rpc(); 13 | title(); 14 | } 15 | 16 | const menu = require(modName + "/menu"); 17 | let dialog; 18 | Events.on(ClientLoadEvent, () => { 19 | dialog = menu.setupDialog(); 20 | menu.addSettings(dialog); 21 | }); 22 | } 23 | 24 | // most pointless thing i have ever done 25 | function rpc(){ 26 | try{ 27 | importPackage(Packages.club.minnced.discord.rpc); 28 | DiscordRPC.INSTANCE.Discord_Shutdown(); 29 | DiscordRPC.INSTANCE.Discord_Initialize("846047005901586432", null, true, "1127400"); 30 | }catch(c){} 31 | } 32 | 33 | // ignore the above comment this is worse 34 | function title(){ 35 | function getGameStatus(){ 36 | if(Vars.state.isGame()){ 37 | if(Vars.net.active()) return "Multiplayer"; 38 | return "Singleplayer"; 39 | } 40 | 41 | if(Vars.ui.editor.isShown()) return "In Editor"; 42 | if(Vars.ui.planet.isShown()) return "In Launch Selection"; 43 | return "In Menu"; 44 | } 45 | 46 | function getGameInfo(){ 47 | switch(getGameStatus()){ 48 | case "In Menu": { 49 | try{ 50 | if(!Core.scene.dialog) return "Main Menu"; 51 | let title = Core.scene.dialog.title.getText(); 52 | return title === "" ? "Unknown" : title; 53 | }catch(c){ 54 | return "File Chooser"; 55 | } 56 | } 57 | case "Singleplayer": 58 | case "Multiplayer": { 59 | let r = Vars.state.rules; 60 | if(r.attackMode) return "Attack"; 61 | if(r.pvp) return "PvP"; 62 | if(r.infiniteResources) return "Sandbox"; 63 | return "Survival"; 64 | } 65 | case "In Launch Selection": { 66 | let p = Vars.ui.planet; 67 | if(!p.selected) return "No Sector Selected"; 68 | if(p.selected.preset) return p.selected.preset.localizedName; 69 | return p.selected.planet.localizedName + " Sector " + p.selected.id; 70 | } 71 | case "In Editor": { 72 | if(!Core.scene.dialog) return "Editing"; 73 | let title = Core.scene.dialog.title.getText(); 74 | if(title === "") return "Editing"; 75 | return title; 76 | } 77 | } 78 | } 79 | 80 | let modcount = Vars.mods.list().copy().filter(e => e.enabled()).size; 81 | let statics = [ 82 | "Mindustry v" + Version.buildString(), 83 | modcount + (modcount === 1 ? " Mod Enabled" : " Mods Enabled"), 84 | ].join(" | "); 85 | 86 | Events.run(Trigger.update, () => { 87 | let dynamics = [ 88 | getGameStatus(), 89 | getGameInfo(), 90 | ].join(" | "); 91 | 92 | Core.graphics.setTitle(statics + " | " + dynamics); 93 | }); 94 | } 95 | -------------------------------------------------------------------------------- /scripts/features/v4/content.js: -------------------------------------------------------------------------------- 1 | importPackage(Packages.arc.util.async); 2 | 3 | function content(){ 4 | const dialog = new BaseDialog("content"); 5 | dialog.addCloseButton(); 6 | 7 | dialog.cont.center().pane(p => { 8 | p.defaults().size(210, 64); 9 | 10 | let i = 0; 11 | Vars.content.each(e => { 12 | if(!(e instanceof UnlockableContent)) return; 13 | p.button(e.localizedName, new TextureRegionDrawable(e.icon(Cicon.medium)), () => { 14 | const content = new BaseDialog(e.name); 15 | content.addCloseButton(); 16 | 17 | let c = content.cont; 18 | c.defaults().center(); 19 | c.image(e.icon(Cicon.full)); 20 | c.row(); 21 | c.label(() => { 22 | let n = e.class.name.includes("$") ? e.class.superclass.name : e.class.name; 23 | return e.localizedName + " (type: " + n.substring(n.lastIndexOf(".") + 1, n.length) + ")"; 24 | }); 25 | c.row(); 26 | c.button("unlock", Icon.lockOpen, () => { 27 | e.quietUnlock(); 28 | content.hide(); 29 | }).size(210, 64); 30 | c.row(); 31 | c.button("unlock temporarily", Icon.lockOpen, () => { 32 | e.alwaysUnlocked = true; 33 | content.hide(); 34 | }).size(210, 64); 35 | c.row(); 36 | c.button("lock", Icon.lock, () => { 37 | e.clearUnlock(); 38 | content.hide(); 39 | }).size(210, 64); 40 | c.row(); 41 | 42 | if(e instanceof Block){ 43 | c.button("build visibility", Icon.eye, () => { 44 | const bv = new BaseDialog("build visibility"); 45 | bv.addCloseButton(); 46 | 47 | Object.keys(BuildVisibility).forEach(b => { 48 | if(BuildVisibility[b] instanceof Function) return; 49 | bv.cont.button(b, () => { 50 | e.buildVisibility = BuildVisibility[b]; 51 | bv.hide(); 52 | }).size(210, 64); 53 | bv.cont.row(); 54 | }); 55 | 56 | bv.show(); 57 | }).size(210, 64); 58 | c.row(); 59 | } 60 | 61 | c.button("more", Icon.add, () => { 62 | const stats = new BaseDialog("stats"); 63 | stats.addCloseButton(); 64 | 65 | stats.cont.center().pane(pane => { 66 | Threads.daemon(() => { 67 | let i2 = 0; 68 | Object.keys(e).forEach(s => { 69 | if(e[s] === undefined) return; 70 | if(typeof e[s] === "object") return; 71 | if(typeof e[s] === "function") return; 72 | 73 | pane.button(s, () => { 74 | Vars.ui.showTextInput("enter value (" + typeof e[s] + ")", s + ":", 128, "", false, v => { 75 | let value = v; 76 | if(v.match(/^true$|^false$/)) value = eval(v); 77 | try{ 78 | e[s] = value; 79 | }catch(c){} 80 | }); 81 | stats.hide(); 82 | }).size(210, 64); 83 | i2++ 84 | if(!(i2 % 2)) pane.row(); 85 | }); 86 | }).join(); 87 | }).growY().width(Vars.mobile ? Core.graphics.getWidth() : Core.graphics.getWidth()/3); 88 | 89 | stats.show(); 90 | }).size(210, 64); 91 | c.row(); 92 | 93 | content.show(); 94 | }); 95 | i++; 96 | if(!(i % 2)) p.row(); 97 | }); 98 | }).growY().width(Vars.mobile ? Core.graphics.getWidth() : Core.graphics.getWidth()/3); 99 | 100 | dialog.buttons.button("more", Icon.add, () => { 101 | const more = new BaseDialog("more"); 102 | more.addCloseButton(); 103 | 104 | let c = more.cont; 105 | c.defaults().size(210, 64); 106 | 107 | c.button("unlock all", () => { 108 | Vars.content.each(e => { 109 | if(!(e instanceof UnlockableContent)) return; 110 | e.quietUnlock(); 111 | }); 112 | more.hide(); 113 | }); 114 | c.row(); 115 | 116 | c.button("temporarily unlock all", () => { 117 | Vars.content.each(e => { 118 | if(!(e instanceof UnlockableContent)) return; 119 | e.alwaysUnlocked = true; 120 | }); 121 | more.hide(); 122 | }); 123 | c.row(); 124 | 125 | c.button("lock all", () => { 126 | Vars.content.each(e => { 127 | if(!(e instanceof UnlockableContent)) return; 128 | e.clearUnlock(); 129 | }); 130 | more.hide(); 131 | }); 132 | c.row(); 133 | 134 | c.button("build visibility\n(all blocks)", Icon.eye, () => { 135 | const bv = new BaseDialog("build visibility"); 136 | bv.addCloseButton(); 137 | 138 | Object.keys(BuildVisibility).forEach(b => { 139 | if(BuildVisibility[b] instanceof Function) return; 140 | bv.cont.button(b, () => { 141 | Vars.content.blocks().each(e => e.buildVisibility = BuildVisibility[b]); 142 | bv.hide(); 143 | }).size(210, 64); 144 | bv.cont.row(); 145 | }); 146 | 147 | bv.show(); 148 | }); 149 | c.row(); 150 | 151 | more.show(); 152 | }); 153 | 154 | dialog.show(); 155 | } 156 | 157 | module.exports = (p) => { 158 | p.button("content", () => { 159 | content(); 160 | }).left().width(210); 161 | }; 162 | -------------------------------------------------------------------------------- /scripts/menu.js: -------------------------------------------------------------------------------- 1 | const features = require(modName + "/features/features"); 2 | let toast = require(modName + "/libs/toast"); 3 | 4 | let loadf = (name) => require(modName + "/features/v4/" + name); 5 | const menus = [ 6 | loadf("worldoptions"), 7 | loadf("content"), 8 | loadf("transform"), 9 | ]; 10 | 11 | function setupDialog(){ 12 | const dialog = new BaseDialog("Hackustry"); 13 | dialog.addCloseButton(); 14 | 15 | // this is P A I N 16 | dialog.cont.center().pane(p => { 17 | p.defaults().height(36); 18 | 19 | p.table(cons(t => { 20 | menus[0](t); 21 | menus[1](t); 22 | })).height(48); 23 | p.row(); 24 | p.table(cons(t => { 25 | menus[2](t); 26 | })).height(48); 27 | p.row(); 28 | 29 | 30 | function add(name, displayName){ 31 | if(!name || typeof name !== "string") return; 32 | if(!displayName || typeof displayName !== "string") displayName = name; 33 | 34 | p.check(displayName, features.get(name), () => { 35 | features.runf(name); 36 | }).left(); 37 | p.row(); 38 | } 39 | 40 | add("reconstructors", "make reconstructors cost nothing and instant"); 41 | add("power-sources", "fix vanilla power sources"); 42 | add("cursed-mode", "cursed mode"); 43 | add("op-turrets", "op turrets"); 44 | add("hackusated-conveyor", "hackusated conveyor"); 45 | add("hackusated-walls", "hackusated walls"); 46 | add("launch-anywhere", "launch anywhere"); 47 | 48 | }).growY().width(Vars.mobile ? Core.graphics.getWidth() : Core.graphics.getWidth()/3); 49 | 50 | dialog.buttons.button("more", Icon.add, () => more()).size(210, 64); 51 | 52 | if(Vars.mobile) dialog.buttons.button(Icon.terminal, () => Vars.ui.scriptfrag.toggle()).size(64); 53 | 54 | return dialog; 55 | } 56 | 57 | function more(){ 58 | const dialog = new BaseDialog("More"); 59 | dialog.addCloseButton(); 60 | 61 | dialog.cont.center().pane(p => { 62 | p.defaults().size(210, 64); 63 | 64 | p.button("Disable all features", Icon.cancel, () => { 65 | const flist = features.features(); 66 | for(let f in flist){ 67 | if(features.get(f)) features.runf(f); 68 | } 69 | Vars.ui.showInfo("The game will now close to disable all features"); 70 | Core.scene.dialog.hidden(() => Core.app.exit()); 71 | }); 72 | p.row(); 73 | 74 | p.button("Import/Export", () => { 75 | const dialog = new BaseDialog("Import/Export"); 76 | dialog.addCloseButton(); 77 | 78 | dialog.cont.center().pane(pane => { 79 | pane.defaults().size(210, 64); 80 | 81 | let includeContent = false; 82 | 83 | pane.check("include content", false, b => includeContent = b); 84 | pane.row(); 85 | 86 | pane.button("Import", () => data(false, includeContent)); 87 | pane.row(); 88 | 89 | pane.button("Export", () => data(true, includeContent)); 90 | pane.row(); 91 | 92 | }).growY().width(Vars.mobile ? Core.graphics.getWidth() : Core.graphics.getWidth()/3); 93 | 94 | dialog.show(); 95 | }); 96 | p.row(); 97 | 98 | p.button("Accounts", Icon.players, () => toast(Icon.info, "not implemented yet")); 99 | p.row(); 100 | 101 | }).growY().width(Vars.mobile ? Core.graphics.getWidth() : Core.graphics.getWidth()/3); 102 | 103 | dialog.show(); 104 | } 105 | 106 | function data(isExport, unlocks){ 107 | if(typeof isExport !== "boolean") return; 108 | if(typeof unlocks !== "boolean") return; 109 | 110 | if(isExport){ 111 | Vars.ui.loadfrag.show(); 112 | 113 | let obj = { 114 | features: {}, 115 | content: {} 116 | }; 117 | 118 | for(let f in features.features()){ 119 | obj.features[f] = features.get(f); 120 | } 121 | 122 | if(unlocks){ 123 | Vars.content.each(e => { 124 | if(!(e instanceof UnlockableContent)) return; 125 | 126 | obj.content[e.name] = [e.alwaysUnlocked, e.unlockedNow()]; 127 | }); 128 | } 129 | 130 | let json = JSON.stringify(obj, null, 4); 131 | 132 | Vars.ui.loadfrag.hide(); 133 | 134 | writeFile("export config", "json", json); 135 | }else{ 136 | readFile("import config", "json", json => { 137 | Vars.ui.loadfrag.show(); 138 | 139 | let obj; 140 | try{ 141 | obj = JSON.parse(json); 142 | }catch(c){ 143 | Vars.ui.loadfrag.hide(); 144 | Vars.ui.showErrorMessage("failed to parse json file"); 145 | return; 146 | } 147 | 148 | let proto = (e) => Object.getPrototypeOf(e); 149 | if(proto(obj.features) !== proto({}) || proto(obj.content) !== proto({})){ 150 | Vars.ui.loadfrag.hide(); 151 | Vars.ui.showErrorMessage("not a valid hackustry config"); 152 | return; 153 | } 154 | 155 | for(let f in obj.features){ 156 | if(obj.features[f] === features.get(f)) continue; 157 | features.runf(f); 158 | } 159 | 160 | if(unlocks){ 161 | Vars.content.each(e => { 162 | if(!(e instanceof UnlockableContent)) return; 163 | if(obj.content[e.name] === undefined) return; 164 | 165 | e.alwaysUnlocked = obj.content[e.name][0]; 166 | obj.content[e.name][1] ? e.quietUnlock() : e.clearUnlock(); 167 | }); 168 | } 169 | 170 | Vars.ui.loadfrag.hide(); 171 | toast(Icon.check, "import successful"); 172 | }); 173 | } 174 | } 175 | 176 | importPackage(Packages.arc.util.serialization); 177 | function randomUUID(){ 178 | try{ 179 | let number = parseInt(Mathf.random(99999999)).toString(); 180 | number = "0".repeat(8 - number.length) + number; 181 | 182 | return Base64Coder.encodeString(number); 183 | }catch(c){ 184 | return randomUUID(); 185 | } 186 | } 187 | 188 | function isValid(uuid){ 189 | if(uuid.match(/^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$/)) return true; 190 | return false; 191 | } 192 | 193 | function accounts(){ 194 | let accArray = [].concat( 195 | Core.settings.getJson( 196 | modName + ".account-list", 197 | Class.forName("[Ljava.lang.Object;"), 198 | () => [{name: Core.settings.getString("name"), uuid: Core.settings.getString("uuid")}] 199 | ) 200 | ); 201 | 202 | const dialog = new BaseDialog("Accounts"); 203 | dialog.addCloseButton(); 204 | 205 | dialog.cont.center().pane(p => { 206 | p.defaults().size(210, 64); 207 | 208 | accArray.forEach(e => { 209 | p.button(e.name, () => { 210 | Core.settings.put("name", e.name); 211 | Core.settings.put("uuid", e.uuid); 212 | }).self(s => s.get().setDisabled(() => Core.settings.getString("uuid") === e.uuid)); 213 | p.row(); 214 | }); 215 | 216 | }).growY().width(Vars.mobile ? Core.graphics.getWidth() : Core.graphics.getWidth()/3); 217 | 218 | dialog.buttons.button("Add Account", Icon.add, () => { 219 | 220 | }); 221 | 222 | dialog.hidden(() => Core.settings.putJson(modName + ".account-list", accArray)); 223 | 224 | dialog.show(); 225 | } 226 | 227 | function addSettings(dialog){ 228 | Vars.ui.settings.shown(() => { 229 | Vars.ui.settings.children.get(1).children.get(0).children.get(0).row(); 230 | Vars.ui.settings.children.get(1).children.get(0).children.get(0).button("Hackustry", Styles.cleart, () => { 231 | dialog.show(); 232 | Vars.ui.settings.hide(); 233 | }); 234 | }); 235 | } 236 | 237 | module.exports = { 238 | setupDialog: setupDialog, 239 | addSettings: addSettings 240 | }; 241 | --------------------------------------------------------------------------------