├── .github └── workflows │ └── github-actions.yml ├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── plugin.json └── src └── sectorized ├── Manager.java ├── SectorizedEvents.java ├── SectorizedPlugin.java ├── constant ├── Config.java ├── Constants.java ├── CoreCost.java ├── DiscordBot.java ├── Loadout.java ├── MenuUtils.java ├── MessageUtils.java ├── RankIcons.java ├── Rules.java ├── StartingBase.java ├── State.java ├── Units.java └── VaultLogic.java ├── faction ├── FactionManager.java ├── core │ ├── Faction.java │ └── Member.java ├── logic │ ├── FactionLogic.java │ └── MemberLogic.java └── persistence │ └── RankingPersistence.java ├── sector ├── SectorManager.java └── core │ ├── GridAccelerator.java │ ├── SectorLogic.java │ ├── SectorSpawns.java │ └── objects │ ├── Border.java │ ├── Rectangle.java │ └── SubRectangle.java ├── update └── UpdateManager.java └── world ├── WorldManager.java └── map ├── Biomes.java ├── BiomesGenerator.java ├── ErekirBiomesGenerator.java ├── MapGenerator.java ├── RiversGenerator.java ├── SerpuloBiomesGenerator.java ├── biomes ├── ErekirBiome.java ├── SerpuloBiome.java ├── erekir │ ├── Arkyic.java │ ├── Beryllic.java │ ├── Carbon.java │ ├── Crystal.java │ ├── Redstone.java │ ├── Regolith.java │ └── Rhyolite.java └── serpulo │ ├── Archipelago.java │ ├── Desert.java │ ├── Grove.java │ ├── Ruins.java │ ├── Salines.java │ ├── Savanna.java │ ├── Swamp.java │ ├── Tundra.java │ └── Vulcano.java └── generator ├── BiomeSelection.java ├── BlockG.java ├── Generator.java ├── SimpleGenerator.java ├── SimplexGenerator2D.java └── SimplexGenerator3D.java /.github/workflows/github-actions.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions 2 | run-name: Build and Deploy 3 | 4 | on: [ push ] 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up JDK 17 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 17 16 | distribution: 'adopt' 17 | - name: Build plugin jar 18 | run: ./gradlew jar 19 | - name: Upload built jar file 20 | uses: actions/upload-artifact@v2 21 | with: 22 | name: ${{ github.event.repository.name }} 23 | path: build/libs/${{ github.event.repository.name }}.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /build 4 | /mindustry-server 5 | /misc 6 | /mindustry-server-v7/ 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sectorized Plugin 2 | 3 | Plugin of the Sectorized Server 4 | 5 | Join the server with `sectorized.freeddns.org` 6 | 7 | Join our discord 8 | server: ![Discord](https://img.shields.io/discord/945026790861176932.svg?logo=discord&logoColor=white&logoWidth=20&labelColor=7289DA&label=Discord&color=17cf48) 9 | 10 | ![image](https://user-images.githubusercontent.com/35230128/162400630-993d70ca-d594-4f32-ac4d-90d7b695e7dd.png) 11 | 12 | The plugin requires several configuration files, if you have troubles setting up the plugin yourself contact me on 13 | discord. -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "java" 2 | 3 | version '1.0' 4 | 5 | sourceCompatibility = JavaVersion.VERSION_16 6 | 7 | sourceSets.main.java.srcDirs = ["src"] 8 | 9 | compileJava.options.encoding = "UTF-8" 10 | compileTestJava.options.encoding = "UTF-8" 11 | 12 | repositories { 13 | mavenCentral() 14 | maven { url "https://raw.githubusercontent.com/Zelaux/MindustryRepo/master/repository" } 15 | maven { url 'https://www.jitpack.io' } 16 | } 17 | 18 | ext { 19 | //the build number that this plugin is made for 20 | mindustryVersion = 'v145' 21 | jabelVersion = "93fde537c7" 22 | } 23 | 24 | //java 8 backwards compatibility flag 25 | allprojects { 26 | tasks.withType(JavaCompile) { 27 | options.compilerArgs.addAll(['--release', '8']) 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation 'org.jetbrains:annotations:20.1.0' 33 | compileOnly "com.github.Anuken.Arc:arc-core:$mindustryVersion" 34 | compileOnly "com.github.Anuken.Mindustry:core:$mindustryVersion" 35 | annotationProcessor "com.github.Anuken:jabel:$jabelVersion" 36 | implementation 'org.mariadb.jdbc:mariadb-java-client:2.1.2' 37 | implementation 'com.google.code.gson:gson:2.9.0' 38 | implementation 'net.dv8tion:JDA:5.0.0-alpha.17' 39 | } 40 | 41 | jar { 42 | archiveFileName = "${project.archivesBaseName}.jar" 43 | from { 44 | configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } 45 | } 46 | 47 | from(rootDir) { 48 | include "plugin.json" 49 | } 50 | 51 | destinationDirectory.set(file("$buildDir/../mindustry-server-v7/config/mods")) 52 | 53 | duplicatesStrategy = DuplicatesStrategy.INCLUDE 54 | } 55 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=--illegal-access=permit \ 2 | --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ 3 | --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ 4 | --add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED \ 5 | --add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ 6 | --add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ 7 | --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ 8 | --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ 9 | --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ 10 | --add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \ 11 | --add-exports=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED \ 12 | --add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ 13 | --add-exports=java.base/sun.reflect.annotation=ALL-UNNAMED -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pointifix/SectorizedPlugin/1b094db5e17d091f0ae8fb0c5240e7a5814ce250/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SectorizedPlugin", 3 | "author": "Pointifix", 4 | "main": "sectorized.SectorizedPlugin", 5 | "description": "Sectorized game mode", 6 | "version": 1.0 7 | } 8 | -------------------------------------------------------------------------------- /src/sectorized/Manager.java: -------------------------------------------------------------------------------- 1 | package sectorized; 2 | 3 | import arc.util.CommandHandler; 4 | 5 | public interface Manager { 6 | void init(); 7 | 8 | void reset(); 9 | 10 | void registerServerCommands(CommandHandler handler); 11 | 12 | void registerClientCommands(CommandHandler handler); 13 | } 14 | -------------------------------------------------------------------------------- /src/sectorized/SectorizedEvents.java: -------------------------------------------------------------------------------- 1 | package sectorized; 2 | 3 | import arc.math.geom.Point2; 4 | import mindustry.game.Team; 5 | import mindustry.world.Tile; 6 | import mindustry.world.blocks.storage.CoreBlock; 7 | import sectorized.faction.core.Faction; 8 | import sectorized.faction.core.Member; 9 | 10 | public class SectorizedEvents { 11 | static { 12 | // create objects to load class (prevents class not found when plugin is updated) 13 | new GamemodeStartEvent(); 14 | new BiomesGeneratedEvent(); 15 | new RestartEvent(""); 16 | new ShutdownEvent(); 17 | new NewMemberEvent(null); 18 | new CoreBuildEvent(null); 19 | new CoreDestroyEvent(null, null); 20 | new MemberSpawnedEvent(null, null); 21 | new NoSpawnPointAvailableEvent(null); 22 | new EliminateFactionEvent(null, null, false); 23 | new TeamDominatingEvent(null); 24 | new NoTeamDominatingEvent(); 25 | } 26 | 27 | public static class GamemodeStartEvent { 28 | public GamemodeStartEvent() { 29 | 30 | } 31 | } 32 | 33 | public static class BiomesGeneratedEvent { 34 | public BiomesGeneratedEvent() { 35 | 36 | } 37 | } 38 | 39 | public static class RestartEvent { 40 | public final String reason; 41 | 42 | public RestartEvent(String reason) { 43 | this.reason = reason; 44 | } 45 | } 46 | 47 | public static class ShutdownEvent { 48 | public ShutdownEvent() { 49 | } 50 | } 51 | 52 | public static class NewMemberEvent { 53 | public final Member member; 54 | 55 | public NewMemberEvent(Member member) { 56 | this.member = member; 57 | } 58 | } 59 | 60 | public static class CoreBuildEvent { 61 | public final Tile tile; 62 | 63 | public CoreBuildEvent(Tile tile) { 64 | this.tile = tile; 65 | } 66 | } 67 | 68 | public static class CoreDestroyEvent { 69 | public final CoreBlock.CoreBuild coreBuild; 70 | public final Faction faction; 71 | 72 | public CoreDestroyEvent(CoreBlock.CoreBuild coreBuild, Faction faction) { 73 | this.coreBuild = coreBuild; 74 | this.faction = faction; 75 | } 76 | } 77 | 78 | public static class MemberSpawnedEvent { 79 | public final Point2 spawnPoint; 80 | public final Member member; 81 | 82 | public MemberSpawnedEvent(Point2 spawnPoint, Member member) { 83 | this.spawnPoint = spawnPoint; 84 | this.member = member; 85 | } 86 | } 87 | 88 | public static class NoSpawnPointAvailableEvent { 89 | public final Member member; 90 | 91 | public NoSpawnPointAvailableEvent(Member member) { 92 | this.member = member; 93 | } 94 | } 95 | 96 | public static class EliminateFactionEvent { 97 | public final Faction defender, attacker; 98 | public final boolean fallback; 99 | 100 | public EliminateFactionEvent(Faction defender, Faction attacker, boolean fallback) { 101 | this.defender = defender; 102 | this.attacker = attacker; 103 | this.fallback = fallback; 104 | } 105 | } 106 | 107 | public static class TeamDominatingEvent { 108 | public final Team team; 109 | 110 | public TeamDominatingEvent(Team team) { 111 | this.team = team; 112 | } 113 | } 114 | 115 | public static class NoTeamDominatingEvent { 116 | public NoTeamDominatingEvent() { 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/sectorized/SectorizedPlugin.java: -------------------------------------------------------------------------------- 1 | package sectorized; 2 | 3 | import arc.Core; 4 | import arc.Events; 5 | import arc.util.CommandHandler; 6 | import arc.util.Log; 7 | import mindustry.core.GameState; 8 | import mindustry.mod.Plugin; 9 | import mindustry.net.Administration; 10 | import sectorized.constant.Config; 11 | import sectorized.constant.DiscordBot; 12 | import sectorized.constant.Rules; 13 | import sectorized.constant.State; 14 | import sectorized.faction.FactionManager; 15 | import sectorized.sector.SectorManager; 16 | import sectorized.update.UpdateManager; 17 | import sectorized.world.WorldManager; 18 | 19 | import java.io.IOException; 20 | import java.net.BindException; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import static arc.util.Log.err; 24 | import static arc.util.Log.info; 25 | import static mindustry.Vars.*; 26 | 27 | public class SectorizedPlugin extends Plugin { 28 | private final Manager[] managers = new Manager[]{ 29 | new WorldManager(), 30 | new FactionManager(), 31 | new SectorManager(), 32 | new UpdateManager() 33 | }; 34 | 35 | @Override 36 | public void init() { 37 | Log.info(Config.c.toString()); 38 | 39 | DiscordBot.init(); 40 | 41 | for (Manager manager : managers) { 42 | manager.init(); 43 | } 44 | } 45 | 46 | @Override 47 | public void registerServerCommands(CommandHandler handler) { 48 | for (Manager manager : managers) { 49 | manager.registerServerCommands(handler); 50 | } 51 | 52 | handler.register("sectorized", "Hosts the sectorized gamemode.", args -> { 53 | logic.reset(); 54 | state.rules = Rules.rules.copy(); 55 | 56 | for (Manager manager : this.managers) { 57 | manager.reset(); 58 | } 59 | 60 | Events.fire(new SectorizedEvents.GamemodeStartEvent()); 61 | Rules.setSpawnGroups(state.rules); 62 | state.rules.infiniteResources = Config.c.infiniteResources; 63 | state.set(GameState.State.paused); 64 | 65 | Core.settings.put("playerlimit", 50); 66 | 67 | while (true) { 68 | try { 69 | net.host(Administration.Config.port.num()); 70 | info("Opened a server on port @.", Administration.Config.port.num()); 71 | break; 72 | } catch (BindException e) { 73 | err("Unable to host: Port " + Administration.Config.port.num() + " already in use! Make sure no other servers are running on the same port in your network."); 74 | state.set(GameState.State.menu); 75 | } catch (IOException e) { 76 | err(e); 77 | state.set(GameState.State.menu); 78 | } 79 | 80 | try { 81 | TimeUnit.SECONDS.sleep(1); 82 | } catch (InterruptedException e) { 83 | e.printStackTrace(); 84 | } 85 | } 86 | 87 | logic.play(); 88 | 89 | State.gameState = State.GameState.ACTIVE; 90 | }); 91 | } 92 | 93 | @Override 94 | public void registerClientCommands(CommandHandler handler) { 95 | for (Manager manager : managers) { 96 | manager.registerClientCommands(handler); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/sectorized/constant/Config.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import com.google.gson.Gson; 4 | 5 | import java.io.IOException; 6 | import java.io.Reader; 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | 10 | public class Config { 11 | public static Config c; 12 | 13 | public final boolean databaseEnabled; 14 | public final boolean updateScoreDecay; 15 | public final boolean discordEnabled; 16 | public final boolean infiniteResources; 17 | 18 | public Config(boolean databaseEnabled, boolean updateScoreDecay, boolean discordEnabled, boolean infiniteResources) { 19 | this.databaseEnabled = databaseEnabled; 20 | this.updateScoreDecay = updateScoreDecay; 21 | this.discordEnabled = discordEnabled; 22 | this.infiniteResources = infiniteResources; 23 | } 24 | 25 | static { 26 | try { 27 | Reader reader = Files.newBufferedReader(Paths.get("config/mods/config/config.json")); 28 | 29 | Config.c = new Gson().fromJson(reader, Config.class); 30 | 31 | reader.close(); 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "Config{" + 40 | "databaseEnabled=" + databaseEnabled + 41 | ", updateScoreDecay=" + updateScoreDecay + 42 | ", discordEnabled=" + discordEnabled + 43 | ", infiniteResources=" + infiniteResources + 44 | '}'; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/sectorized/constant/Constants.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import mindustry.content.Blocks; 4 | import mindustry.world.blocks.storage.CoreBlock; 5 | 6 | import java.util.HashMap; 7 | 8 | public class Constants { 9 | public static final int mapWidth = 600, mapHeight = 600; 10 | 11 | public static final int spawnCellSize = 50; 12 | 13 | public static final HashMap radii = new HashMap() {{ 14 | put(Blocks.coreShard, 28); 15 | put(Blocks.coreFoundation, 33); 16 | put(Blocks.coreNucleus, 38); 17 | put(Blocks.coreBastion, 30); 18 | put(Blocks.coreCitadel, 38); 19 | put(Blocks.coreAcropolis, 42); 20 | }}; 21 | } 22 | -------------------------------------------------------------------------------- /src/sectorized/constant/CoreCost.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import mindustry.content.Items; 4 | import mindustry.content.Planets; 5 | import mindustry.game.Team; 6 | import mindustry.type.Item; 7 | import mindustry.type.ItemSeq; 8 | 9 | import java.util.HashMap; 10 | 11 | public class CoreCost { 12 | public static final HashMap itemUnicodes = new HashMap() {{ 13 | put(Items.copper, "\uF838"); 14 | put(Items.lead, "\uF837"); 15 | put(Items.graphite, "\uF835"); 16 | put(Items.silicon, "\uF82F"); 17 | put(Items.metaglass, "\uF836"); 18 | put(Items.titanium, "\uF832"); 19 | put(Items.thorium, "\uF831"); 20 | put(Items.plastanium, "\uF82E"); 21 | put(Items.phaseFabric, "\uF82D"); 22 | put(Items.surgeAlloy, "\uF82C"); 23 | put(Items.beryllium, "\uF748"); 24 | put(Items.tungsten, "\uF739"); 25 | put(Items.oxide, "\uF721"); 26 | put(Items.carbide, "\uF736"); 27 | }}; 28 | 29 | private static final int size = 25; 30 | private static final int maxTeamSize = 4; 31 | 32 | public static final ItemSeq[][] requirementsSerpulo = new ItemSeq[size][maxTeamSize]; 33 | public static final ItemSeq[][] requirementsErekir = new ItemSeq[size][maxTeamSize]; 34 | 35 | static { 36 | double factor = 0; 37 | 38 | for (int teamSize = 0; teamSize < maxTeamSize; teamSize++) { 39 | factor += 1d / Math.pow(2, (Math.max(teamSize - 1, 0))); 40 | 41 | for (int i = 0; i < size; i++) { 42 | ItemSeq itemSeq = new ItemSeq(); 43 | 44 | itemSeq.add(Items.copper, (int) ((200 + i * 100) * factor)); 45 | itemSeq.add(Items.lead, (int) ((100 + i * 70) * factor)); 46 | if (i >= 1) itemSeq.add(Items.graphite, (int) ((50 + (i - 1) * 20) * factor)); 47 | if (i >= 2) itemSeq.add(Items.silicon, (int) ((70 + (i - 2) * 50) * factor)); 48 | if (i >= 3) itemSeq.add(Items.metaglass, (int) ((50 + (i - 3) * 30) * factor)); 49 | if (i >= 5) itemSeq.add(Items.titanium, (int) ((200 + (i - 5) * 40) * factor)); 50 | if (i >= 6) itemSeq.add(Items.thorium, (int) ((100 + (i - 6) * 40) * factor)); 51 | if (i >= 8) itemSeq.add(Items.plastanium, (int) ((50 + (i - 8) * 30) * factor)); 52 | if (i >= 11) itemSeq.add(Items.phaseFabric, (int) ((20 + (i - 11) * 20) * factor)); 53 | if (i >= 15) itemSeq.add(Items.surgeAlloy, (int) ((30 + (i - 15) * 30) * factor)); 54 | 55 | requirementsSerpulo[i][teamSize] = itemSeq; 56 | } 57 | 58 | for (int i = 0; i < size; i++) { 59 | ItemSeq itemSeq = new ItemSeq(); 60 | 61 | itemSeq.add(Items.beryllium, (int) ((50 + i * 50) * factor)); 62 | if (i >= 1) itemSeq.add(Items.graphite, (int) ((20 + (i - 1) * 20) * factor)); 63 | if (i >= 3) itemSeq.add(Items.silicon, (int) ((50 + (i - 3) * 30) * factor)); 64 | if (i >= 5) itemSeq.add(Items.oxide, (int) ((10 + (i - 5) * 20) * factor)); 65 | if (i >= 7) itemSeq.add(Items.carbide, (int) ((10 + (i - 7) * 10) * factor)); 66 | 67 | requirementsErekir[i][teamSize] = itemSeq; 68 | } 69 | } 70 | } 71 | 72 | public static boolean checkAndConsumeFunds(Team team) { 73 | int core = Math.max(Math.min(team.cores().size - 1, size - 1), 0); 74 | int size = Math.max(Math.min(team.data().players.size - 1, maxTeamSize - 1), 0); 75 | 76 | ItemSeq requirement = State.planet.equals(Planets.serpulo.name) ? requirementsSerpulo[core][size] : requirementsErekir[core][size]; 77 | 78 | if (team.core().items().has(requirement)) { 79 | team.core().items().remove(requirement); 80 | return true; 81 | } 82 | 83 | return false; 84 | } 85 | 86 | public static ItemSeq getRequirements(Team team) { 87 | int core = Math.max(Math.min(team.cores().size - 1, size - 1), 0); 88 | int size = Math.max(Math.min(team.data().players.size - 1, maxTeamSize - 1), 0); 89 | 90 | return State.planet.equals(Planets.serpulo.name) ? requirementsSerpulo[core][size] : requirementsErekir[core][size]; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/sectorized/constant/DiscordBot.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import arc.util.Strings; 4 | import com.google.gson.Gson; 5 | import net.dv8tion.jda.api.JDA; 6 | import net.dv8tion.jda.api.JDABuilder; 7 | import net.dv8tion.jda.api.entities.*; 8 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 9 | import net.dv8tion.jda.api.hooks.ListenerAdapter; 10 | import net.dv8tion.jda.api.requests.GatewayIntent; 11 | import net.dv8tion.jda.api.utils.ChunkingFilter; 12 | import net.dv8tion.jda.api.utils.MemberCachePolicy; 13 | 14 | import javax.security.auth.login.LoginException; 15 | import java.io.IOException; 16 | import java.io.Reader; 17 | import java.nio.file.Files; 18 | import java.nio.file.Paths; 19 | import java.util.HashMap; 20 | 21 | public class DiscordBot { 22 | private static JDA bot; 23 | private static Guild guild; 24 | private static TextChannel log; 25 | private static TextChannel hallOfFame; 26 | 27 | private static final HashMap awaitConfirmMessage = new HashMap<>(); 28 | 29 | public static void init() { 30 | if (Config.c.discordEnabled) { 31 | try { 32 | Gson gson = new Gson(); 33 | Reader reader = Files.newBufferedReader(Paths.get("config/mods/config/discordConfig.json")); 34 | DiscordConfig config = gson.fromJson(reader, DiscordConfig.class); 35 | reader.close(); 36 | 37 | DiscordBot.bot = JDABuilder.createDefault(config.token) 38 | .setChunkingFilter(ChunkingFilter.ALL) 39 | .setMemberCachePolicy(MemberCachePolicy.ALL) 40 | .enableIntents(GatewayIntent.GUILD_MEMBERS) 41 | .build() 42 | .awaitReady(); 43 | 44 | DiscordBot.guild = DiscordBot.bot.getGuildById(config.guildID); 45 | 46 | DiscordBot.log = DiscordBot.bot.getTextChannelById(config.logChannelID); 47 | DiscordBot.hallOfFame = DiscordBot.bot.getTextChannelById(config.hallOfFameChannelID); 48 | 49 | DiscordBot.bot.addEventListener(new MessageListener()); 50 | } catch (LoginException | InterruptedException | IOException e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | } 55 | 56 | public static void sendMessage(String message) { 57 | if (Config.c.discordEnabled) { 58 | log.sendMessage(message).queue(); 59 | } 60 | } 61 | 62 | public static void sendMessageToHallOfFame(String message) { 63 | if (Config.c.discordEnabled) { 64 | hallOfFame.sendMessage(message).queue(); 65 | } 66 | } 67 | 68 | public static void editLastMessageInHallOfFame(String message) { 69 | if (Config.c.discordEnabled) { 70 | hallOfFame.getHistory().retrievePast(1).queue(messages -> { 71 | if (messages.size() == 1) { 72 | Message m = messages.get(0); 73 | 74 | if (m.getAuthor().isBot()) m.editMessage(message).queue(); 75 | } 76 | }); 77 | } 78 | } 79 | 80 | public static void setStatus(String status) { 81 | if (Config.c.discordEnabled) { 82 | DiscordBot.bot.getPresence().setActivity(Activity.playing(status)); 83 | } 84 | } 85 | 86 | public static boolean checkIfExists(String tag) { 87 | if (Config.c.discordEnabled) { 88 | return guild.getMemberByTag(tag) != null; 89 | } 90 | return false; 91 | } 92 | 93 | public static void register(String tag, sectorized.faction.core.Member sectorizedMember) { 94 | if (Config.c.discordEnabled) { 95 | Member member = guild.getMemberByTag(tag); 96 | 97 | if (member != null) { 98 | awaitConfirmMessage.put(member.getUser().getAsTag(), sectorizedMember); 99 | 100 | member.getUser().openPrivateChannel().queue(privateChannel -> { 101 | privateChannel.sendMessage("Was that you? \nA user named *" + Strings.stripColors(sectorizedMember.player.name).substring(1).replace("@", "at") + "* requested to link this account, type **yes** to confirm! \nIf that was not you please ignore this message!").queue(); 102 | }); 103 | 104 | MessageUtils.sendMessage(sectorizedMember.player, "Check your Discord, you should have received a message from the " + MessageUtils.cHighlight1 + "SectorizedBot" + MessageUtils.cDefault + ". \nIf not, you probably have to adjust your settings to allow messages from other server members.", MessageUtils.MessageLevel.INFO); 105 | } 106 | } else { 107 | MessageUtils.sendMessage(sectorizedMember.player, "Discord currently disabled", MessageUtils.MessageLevel.WARNING); 108 | } 109 | } 110 | 111 | private static class DiscordConfig { 112 | public String token; 113 | public long guildID; 114 | public long logChannelID; 115 | public long hallOfFameChannelID; 116 | 117 | public DiscordConfig(String token, long guildID, long logChannelID, long hallOfFameChannelID) { 118 | this.token = token; 119 | this.guildID = guildID; 120 | this.logChannelID = logChannelID; 121 | this.hallOfFameChannelID = hallOfFameChannelID; 122 | } 123 | } 124 | 125 | private static class MessageListener extends ListenerAdapter { 126 | public void onMessageReceived(MessageReceivedEvent event) { 127 | if (awaitConfirmMessage.containsKey(event.getAuthor().getAsTag()) && event.getMessage().getContentDisplay().equalsIgnoreCase("yes")) { 128 | sectorized.faction.core.Member sectorizedMember = awaitConfirmMessage.get(event.getAuthor().getAsTag()); 129 | 130 | sectorizedMember.discordTag = event.getAuthor().getAsTag(); 131 | 132 | Member guildMember = guild.getMemberByTag(sectorizedMember.discordTag); 133 | 134 | if (guildMember != null) { 135 | DiscordBot.assignRole(sectorizedMember); 136 | 137 | guildMember.getUser().openPrivateChannel().queue(privateChannel -> { 138 | privateChannel.sendMessage("Successfully linked your ingame account!").queue(); 139 | }); 140 | } 141 | } 142 | } 143 | } 144 | 145 | public static void assignRole(sectorized.faction.core.Member sectorizedMember) { 146 | if (Config.c.discordEnabled) { 147 | if (sectorizedMember.discordTag != null) { 148 | Member guildMember = DiscordBot.guild.getMemberByTag(sectorizedMember.discordTag); 149 | 150 | if (guildMember != null) { 151 | String roleName; 152 | int rank = sectorizedMember.rank; 153 | 154 | if (rank > 500 || rank == -1) roleName = "other"; 155 | else if (rank > 200) roleName = "top 500"; 156 | else if (rank > 100) roleName = "top 200"; 157 | else if (rank > 50) roleName = "top 100"; 158 | else if (rank > 25) roleName = "top 50"; 159 | else if (rank > 10) roleName = "top 25"; 160 | else roleName = "top 10"; 161 | 162 | Role role = null; 163 | for (Role r : DiscordBot.guild.getRoles()) { 164 | if (r.getName().equals(roleName)) { 165 | role = r; 166 | } 167 | } 168 | 169 | if (role != null) { 170 | for (Role guildMemberRole : guildMember.getRoles()) { 171 | if (!guildMemberRole.getName().equals(roleName)) 172 | DiscordBot.guild.removeRoleFromMember(guildMember, guildMemberRole).queue(); 173 | } 174 | 175 | DiscordBot.guild.addRoleToMember(guildMember, role).queue(); 176 | } 177 | } 178 | } 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/sectorized/constant/Loadout.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import arc.struct.Seq; 4 | import mindustry.content.Items; 5 | import mindustry.content.Planets; 6 | import mindustry.type.ItemStack; 7 | 8 | public class Loadout { 9 | public static Seq getLoadout(int wave) { 10 | wave--; 11 | 12 | if (State.planet.equals(Planets.serpulo.name)) { 13 | Seq loadout = ItemStack.list( 14 | Items.copper, 800 + (150 * wave), 15 | Items.lead, 500 + (100 * wave), 16 | Items.graphite, 150 + (20 * wave), 17 | Items.silicon, 150 + (30 * wave), 18 | Items.metaglass, 100 + (10 * wave), 19 | Items.titanium, 50 + (20 * wave), 20 | Items.thorium, 10 + (15 * wave)); 21 | 22 | if (wave >= 5) loadout.add(new ItemStack(Items.plastanium, 20 * (wave - 4))); 23 | if (wave >= 10) loadout.add(new ItemStack(Items.phaseFabric, 15 * (wave - 9))); 24 | if (wave >= 10) loadout.add(new ItemStack(Items.surgeAlloy, 15 * (wave - 9))); 25 | 26 | return loadout; 27 | } else if (State.planet.equals(Planets.erekir.name)) { 28 | Seq loadout = ItemStack.list( 29 | Items.beryllium, 300 + (100 * wave), 30 | Items.graphite, 100 + (50 * wave), 31 | Items.silicon, 50 + (30 * wave), 32 | Items.thorium, 150 + (20 * wave)); 33 | 34 | if (wave >= 1) loadout.add(new ItemStack(Items.tungsten, 30 * (wave))); 35 | if (wave >= 3) loadout.add(new ItemStack(Items.oxide, 20 * (wave - 2))); 36 | if (wave >= 5) loadout.add(new ItemStack(Items.carbide, 10 * (wave - 4))); 37 | if (wave >= 7) loadout.add(new ItemStack(Items.surgeAlloy, 20 * (wave - 7))); 38 | if (wave >= 9) loadout.add(new ItemStack(Items.phaseFabric, 10 * (wave - 4))); 39 | 40 | return loadout; 41 | } 42 | 43 | return new Seq<>(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/sectorized/constant/MenuUtils.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import arc.Events; 4 | import arc.func.Func; 5 | import arc.struct.IntMap; 6 | import mindustry.game.EventType; 7 | import mindustry.gen.Call; 8 | import mindustry.gen.Player; 9 | 10 | import java.util.HashMap; 11 | 12 | public class MenuUtils { 13 | static IntMap> menus = new IntMap<>(); 14 | 15 | static HashMap lastContents = new HashMap<>(); 16 | 17 | static { 18 | Events.on(EventType.MenuOptionChooseEvent.class, event -> { 19 | if (menus.containsKey(event.menuId)) { 20 | MenuContent current = lastContents.get(event.player); 21 | 22 | Handler handler = current.getAction(event.option); 23 | 24 | int nextMenuId = current.getLink(event.option); 25 | 26 | if (handler == null || (current.menuId / 10) != (event.menuId / 10)) return; 27 | 28 | if (nextMenuId >= 0) { 29 | Func nextMenu = menus.get(nextMenuId); 30 | MenuContent next = nextMenu.get(event.player); 31 | next.menuId = nextMenuId; 32 | 33 | lastContents.put(event.player, next); 34 | Call.menu(event.player.con(), nextMenuId, next.title, next.message, next.options); 35 | } 36 | 37 | handler.get(event.player); 38 | } 39 | }); 40 | } 41 | 42 | public static void addMenu(int menuId, Func contentFunc) { 43 | if (menus.containsKey(menuId)) { 44 | throw new IllegalStateException("Menu ID already exists!"); 45 | } 46 | 47 | MenuUtils.menus.put(menuId, contentFunc); 48 | } 49 | 50 | public static void showMenu(int menuId, Player player) { 51 | if (!menus.containsKey(menuId)) { 52 | throw new IllegalStateException("Menu ID not found!"); 53 | } 54 | 55 | Func menu = menus.get(menuId); 56 | MenuContent content = menu.get(player); 57 | content.menuId = menuId; 58 | 59 | lastContents.put(player, content); 60 | Call.menu(player.con(), menuId, content.title, content.message, content.options); 61 | } 62 | 63 | public static class MenuContent { 64 | public int menuId; 65 | public String title; 66 | public String message; 67 | public String[][] options; 68 | public int[][] links; 69 | public Handler[][] actions; 70 | 71 | public MenuContent(String title, String message, String[][] options, int[][] links, Handler[][] actions) { 72 | this.title = title; 73 | this.message = message; 74 | this.options = options; 75 | this.links = links; 76 | this.actions = actions; 77 | } 78 | 79 | public int getLink(int option) { 80 | int o = 0; 81 | for (int i = 0; i < this.links.length; i++) { 82 | for (int j = 0; j < this.links[i].length; j++) { 83 | if (o == option) return this.links[i][j]; 84 | 85 | o++; 86 | } 87 | } 88 | 89 | return -1; 90 | } 91 | 92 | public Handler getAction(int option) { 93 | int o = 0; 94 | for (int i = 0; i < this.actions.length; i++) { 95 | for (int j = 0; j < this.actions[i].length; j++) { 96 | if (o == option) return this.actions[i][j]; 97 | 98 | o++; 99 | } 100 | } 101 | 102 | return null; 103 | } 104 | } 105 | 106 | public interface Handler { 107 | void get(Player player); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/sectorized/constant/MessageUtils.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import arc.util.Timer; 4 | import mindustry.gen.Call; 5 | import mindustry.gen.Player; 6 | 7 | import java.util.ArrayList; 8 | import java.util.EnumMap; 9 | 10 | public class MessageUtils { 11 | public static final String cDefault = "[#dddddd]"; // lightgray 12 | public static final String cInfo = "[#33c9ff]"; // cyan 13 | public static final String cWarning = "[#ffa733]"; // orange 14 | public static final String cDanger = "[#ff3336]"; // red 15 | public static final String cPlayer = "[#335fff]"; // blue 16 | public static final String cHighlight1 = "[#ffe433]"; // gold 17 | public static final String cHighlight2 = "[#33ff5c]"; // light green 18 | public static final String cHighlight3 = "[#f533ff]"; // magenta 19 | 20 | private static final ArrayList bufferedMessages = new ArrayList<>(); 21 | private static final EnumMap messageLevelPrefixes = new EnumMap<>(MessageLevel.class); 22 | 23 | static { 24 | messageLevelPrefixes.put(MessageLevel.INFO, cInfo + "\uE837 " + cDefault); 25 | messageLevelPrefixes.put(MessageLevel.WARNING, cWarning + "\u26A0 " + cDefault); 26 | messageLevelPrefixes.put(MessageLevel.ELIMINATION, cDanger + "\uE861 " + cDefault); 27 | 28 | messageLevelPrefixes.put(MessageLevel.ATTACK, cDanger + "\uE865 " + cDefault); 29 | messageLevelPrefixes.put(MessageLevel.DEFEND, cWarning + "\uE86B " + cDefault); 30 | messageLevelPrefixes.put(MessageLevel.IDLE, cInfo + "\uE86C " + cDefault); 31 | } 32 | 33 | public static void sendMessage(String message, MessageLevel level) { 34 | Call.sendMessage(messageLevelPrefixes.get(level) + message); 35 | } 36 | 37 | public static void sendMessage(Player player, String message, MessageLevel level) { 38 | player.sendMessage(messageLevelPrefixes.get(level) + message); 39 | } 40 | 41 | public static void sendBufferedMessage(Player player, String message, MessageLevel level, int seconds) { 42 | if (!bufferedMessages.contains(player.id)) { 43 | bufferedMessages.add(player.id); 44 | 45 | player.sendMessage(messageLevelPrefixes.get(level) + message); 46 | 47 | Timer.schedule(() -> bufferedMessages.remove(Integer.valueOf(player.id)), seconds); 48 | } 49 | } 50 | 51 | public static void sendBufferedMessage(Player player, String message, MessageLevel level) { 52 | sendBufferedMessage(player, message, level, 3); 53 | } 54 | 55 | public enum MessageLevel { 56 | INFO, 57 | WARNING, 58 | ELIMINATION, 59 | ATTACK, 60 | DEFEND, 61 | IDLE 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/sectorized/constant/RankIcons.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | public class RankIcons { 4 | public static String getRankIcon(int rank) { 5 | String icon = "[white]"; 6 | 7 | if (rank > 500 || rank == -1) icon += "\uF7AD"; 8 | else if (rank > 200) icon += "\uF7B2"; 9 | else if (rank > 100) icon += "\uF7B0"; 10 | else if (rank > 50) icon += "\uF7B6"; 11 | else if (rank > 25) icon += "\uF7A8"; 12 | else if (rank > 10) icon += "\uF7A7"; 13 | else icon += "\uF7AB"; 14 | 15 | return icon; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/sectorized/constant/Rules.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import arc.struct.ObjectSet; 4 | import arc.struct.Seq; 5 | import mindustry.Vars; 6 | import mindustry.content.Blocks; 7 | import mindustry.content.Planets; 8 | import mindustry.game.SpawnGroup; 9 | import mindustry.game.Team; 10 | import mindustry.world.blocks.defense.turrets.ItemTurret; 11 | import mindustry.world.blocks.defense.turrets.LaserTurret; 12 | import mindustry.world.blocks.units.Reconstructor; 13 | 14 | import static mindustry.content.UnitTypes.*; 15 | 16 | public class Rules { 17 | public static final mindustry.game.Rules rules = new mindustry.game.Rules(); 18 | 19 | static { 20 | rules.tags.put("sectorized", "true"); 21 | rules.enemyCoreBuildRadius = 0.0f; 22 | rules.canGameOver = false; 23 | rules.defaultTeam = Team.derelict; 24 | rules.waves = true; 25 | rules.pvp = true; 26 | Vars.state.gameOver = true; 27 | rules.waitEnemies = false; 28 | rules.buildSpeedMultiplier = 2.0f; 29 | rules.blockDamageMultiplier = 2.0f; 30 | rules.unitDamageMultiplier = 0.5f; 31 | rules.buildCostMultiplier = 1f; 32 | rules.dropZoneRadius = 100f; 33 | rules.logicUnitBuild = false; 34 | rules.coreIncinerates = true; 35 | rules.possessionAllowed = true; 36 | rules.showSpawns = true; 37 | rules.bannedBlocks = ObjectSet.with( 38 | Blocks.shockMine, 39 | Blocks.switchBlock, 40 | Blocks.hyperProcessor, 41 | Blocks.logicProcessor, 42 | Blocks.microProcessor, 43 | Blocks.memoryCell, 44 | Blocks.memoryBank, 45 | Blocks.logicDisplay, 46 | Blocks.largeLogicDisplay, 47 | Blocks.canvas); 48 | 49 | ((ItemTurret) (Blocks.foreshadow)).ammoTypes.forEach(ammoType -> { 50 | ammoType.value.damage *= 1.5; 51 | }); 52 | ((ItemTurret) (Blocks.spectre)).ammoTypes.forEach(ammoType -> { 53 | ammoType.value.damage *= 2.0; 54 | }); 55 | ((LaserTurret) (Blocks.meltdown)).shootType.damage *= 1.5; 56 | ((Reconstructor) (Blocks.exponentialReconstructor)).constructTime *= 0.75; 57 | ((Reconstructor) (Blocks.tetrativeReconstructor)).constructTime *= 0.75; 58 | 59 | rules.unitCap = 0; 60 | 61 | Blocks.coreShard.unitCapModifier = 4; 62 | Blocks.coreFoundation.unitCapModifier = 6; 63 | Blocks.coreNucleus.unitCapModifier = 8; 64 | 65 | Blocks.coreBastion.unitCapModifier = 3; 66 | Blocks.coreCitadel.unitCapModifier = 5; 67 | Blocks.coreAcropolis.unitCapModifier = 7; 68 | } 69 | 70 | public static void setSpawnGroups(mindustry.game.Rules rules) { 71 | if (State.planet.equals(Planets.serpulo.name)) { 72 | rules.waveSpacing = 60 * 60 * 3.0f; 73 | 74 | rules.spawns = Seq.with( 75 | // T1 76 | new SpawnGroup(dagger) {{ 77 | begin = 0; 78 | end = 9; 79 | unitAmount = 1; 80 | unitScaling = 1; 81 | }}, 82 | new SpawnGroup(nova) {{ 83 | begin = 5; 84 | end = 9; 85 | unitAmount = 1; 86 | unitScaling = 1; 87 | }}, 88 | new SpawnGroup(crawler) {{ 89 | begin = 3; 90 | end = 9; 91 | unitAmount = 1; 92 | unitScaling = 1; 93 | }}, 94 | new SpawnGroup(flare) {{ 95 | begin = 5; 96 | end = 9; 97 | unitAmount = 1; 98 | unitScaling = 1; 99 | }}, 100 | new SpawnGroup(risso) {{ 101 | begin = 5; 102 | end = 9; 103 | spacing = 2; 104 | unitAmount = 1; 105 | unitScaling = 0.5f; 106 | }}, 107 | 108 | // T2 109 | new SpawnGroup(mace) {{ 110 | begin = 10; 111 | end = 19; 112 | unitAmount = 1; 113 | unitScaling = 1; 114 | }}, 115 | new SpawnGroup(pulsar) {{ 116 | begin = 15; 117 | end = 19; 118 | unitAmount = 1; 119 | unitScaling = 1; 120 | }}, 121 | new SpawnGroup(atrax) {{ 122 | begin = 13; 123 | end = 19; 124 | unitAmount = 1; 125 | unitScaling = 1; 126 | }}, 127 | new SpawnGroup(horizon) {{ 128 | begin = 15; 129 | end = 19; 130 | unitAmount = 1; 131 | unitScaling = 1; 132 | }}, 133 | new SpawnGroup(minke) {{ 134 | begin = 15; 135 | end = 19; 136 | spacing = 2; 137 | unitAmount = 1; 138 | unitScaling = 1; 139 | }}, 140 | 141 | // T3 142 | new SpawnGroup(fortress) {{ 143 | begin = 20; 144 | end = 29; 145 | unitAmount = 1; 146 | unitScaling = 1; 147 | }}, 148 | new SpawnGroup(quasar) {{ 149 | begin = 25; 150 | end = 29; 151 | unitAmount = 1; 152 | unitScaling = 1; 153 | }}, 154 | new SpawnGroup(spiroct) {{ 155 | begin = 23; 156 | end = 29; 157 | unitAmount = 1; 158 | unitScaling = 1; 159 | }}, 160 | new SpawnGroup(zenith) {{ 161 | begin = 25; 162 | end = 29; 163 | unitAmount = 1; 164 | unitScaling = 1; 165 | }}, 166 | new SpawnGroup(bryde) {{ 167 | begin = 25; 168 | end = 29; 169 | spacing = 2; 170 | unitAmount = 1; 171 | unitScaling = 2f; 172 | }}, 173 | 174 | // T4 175 | new SpawnGroup(scepter) {{ 176 | begin = 30; 177 | end = 39; 178 | unitAmount = 1; 179 | unitScaling = 2f; 180 | }}, 181 | new SpawnGroup(vela) {{ 182 | begin = 35; 183 | end = 39; 184 | unitAmount = 1; 185 | unitScaling = 2f; 186 | }}, 187 | new SpawnGroup(arkyid) {{ 188 | begin = 33; 189 | end = 39; 190 | unitAmount = 1; 191 | unitScaling = 2f; 192 | }}, 193 | new SpawnGroup(antumbra) {{ 194 | begin = 35; 195 | end = 39; 196 | unitAmount = 1; 197 | unitScaling = 2f; 198 | }}, 199 | new SpawnGroup(quad) {{ 200 | begin = 37; 201 | end = 39; 202 | unitAmount = 1; 203 | unitScaling = 4f; 204 | }}, 205 | new SpawnGroup(sei) {{ 206 | begin = 35; 207 | end = 39; 208 | spacing = 2; 209 | unitAmount = 1; 210 | unitScaling = 4f; 211 | }}, 212 | 213 | // T5 214 | new SpawnGroup(reign) {{ 215 | begin = 40; 216 | end = never; 217 | unitAmount = 2; 218 | unitScaling = 5f; 219 | }}, 220 | new SpawnGroup(corvus) {{ 221 | begin = 45; 222 | end = never; 223 | unitAmount = 2; 224 | unitScaling = 5f; 225 | }}, 226 | new SpawnGroup(toxopid) {{ 227 | begin = 50; 228 | end = never; 229 | unitAmount = 2; 230 | unitScaling = 5f; 231 | }}, 232 | new SpawnGroup(eclipse) {{ 233 | begin = 55; 234 | end = never; 235 | unitAmount = 2; 236 | unitScaling = 5f; 237 | }}, 238 | new SpawnGroup(omura) {{ 239 | begin = 55; 240 | end = never; 241 | unitAmount = 2; 242 | unitScaling = 5f; 243 | }} 244 | ); 245 | } else if (State.planet.equals(Planets.erekir.name)) { 246 | rules.waveSpacing = 60 * 60 * 3f; 247 | 248 | rules.spawns = Seq.with( 249 | // T1 250 | new SpawnGroup(stell) {{ 251 | begin = 0; 252 | end = 14; 253 | unitAmount = 1; 254 | unitScaling = 3f; 255 | }}, 256 | new SpawnGroup(merui) {{ 257 | begin = 5; 258 | end = 7; 259 | unitAmount = 1; 260 | unitScaling = 3f; 261 | }}, 262 | new SpawnGroup(elude) {{ 263 | begin = 11; 264 | end = 14; 265 | unitAmount = 1; 266 | unitScaling = 3f; 267 | }}, 268 | // T2 269 | new SpawnGroup(locus) {{ 270 | begin = 15; 271 | end = 29; 272 | unitAmount = 1; 273 | unitScaling = 3f; 274 | }}, 275 | new SpawnGroup(anthicus) {{ 276 | begin = 22; 277 | end = 29; 278 | unitAmount = 1; 279 | unitScaling = 3f; 280 | }}, 281 | new SpawnGroup(avert) {{ 282 | begin = 26; 283 | end = 29; 284 | unitAmount = 1; 285 | unitScaling = 3f; 286 | }}, 287 | // T3 288 | new SpawnGroup(precept) {{ 289 | begin = 30; 290 | end = 44; 291 | unitAmount = 1; 292 | unitScaling = 3f; 293 | }}, 294 | new SpawnGroup(tecta) {{ 295 | begin = 37; 296 | end = 44; 297 | unitAmount = 1; 298 | unitScaling = 3f; 299 | }}, 300 | new SpawnGroup(obviate) {{ 301 | begin = 41; 302 | end = 44; 303 | unitAmount = 1; 304 | unitScaling = 3f; 305 | }}, 306 | // T4 307 | new SpawnGroup(vanquish) {{ 308 | begin = 45; 309 | end = 59; 310 | unitAmount = 1; 311 | unitScaling = 3f; 312 | }}, 313 | new SpawnGroup(collaris) {{ 314 | begin = 52; 315 | end = 59; 316 | unitAmount = 1; 317 | unitScaling = 3f; 318 | }}, 319 | new SpawnGroup(quell) {{ 320 | begin = 54; 321 | end = 59; 322 | unitAmount = 1; 323 | unitScaling = 3f; 324 | }}, 325 | // T5 326 | new SpawnGroup(conquer) {{ 327 | begin = 56; 328 | end = never; 329 | unitAmount = 1; 330 | unitScaling = 5f; 331 | }}, 332 | new SpawnGroup(disrupt) {{ 333 | begin = 58; 334 | end = never; 335 | unitAmount = 1; 336 | unitScaling = 5f; 337 | }} 338 | ); 339 | } 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/sectorized/constant/StartingBase.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import mindustry.content.Blocks; 4 | import mindustry.content.Planets; 5 | import mindustry.game.Schematic; 6 | import mindustry.game.Schematics; 7 | import mindustry.game.Team; 8 | import mindustry.gen.Call; 9 | import mindustry.type.ItemStack; 10 | import mindustry.world.Tile; 11 | import mindustry.world.blocks.storage.CoreBlock; 12 | 13 | import static mindustry.Vars.state; 14 | import static mindustry.Vars.world; 15 | 16 | public class StartingBase { 17 | public static void spawnStartingBase(int coreX, int coreY, Team team) { 18 | Schematic start = getStartingBase(state.wave); 19 | 20 | Schematic.Stile coreTile = start.tiles.find(s -> s.block instanceof CoreBlock); 21 | if (coreTile == null) throw new IllegalArgumentException("Schematic has no core tile."); 22 | int ox = coreX - coreTile.x, oy = coreY - coreTile.y; 23 | start.tiles.each(st -> { 24 | Tile tile = world.tile(st.x + ox, st.y + oy); 25 | if (tile == null) return; 26 | 27 | if (tile.block() != Blocks.air) { 28 | tile.removeNet(); 29 | } 30 | 31 | tile.setNet(st.block, team, st.rotation); 32 | 33 | if (st.config != null) { 34 | tile.build.configureAny(st.config); 35 | } 36 | if (tile.block() instanceof CoreBlock) { 37 | for (ItemStack stack : state.rules.loadout) { 38 | Call.setItem(tile.build, stack.item, stack.amount); 39 | } 40 | } 41 | }); 42 | } 43 | 44 | private static Schematic getStartingBase(int wave) { 45 | if (State.planet.equals(Planets.serpulo.name)) { 46 | if (wave >= 20) { 47 | return Schematics.readBase64("bXNjaAF4nE2Uy27cRhBFa5rv90OJk5+YZb7EyyALitNyCFDkgORIVj422yC/kAia1K1rwLFgHg7Zfaq6utjyIA+BhMvw7KX97Mdj3aY//OXzdXhdfpHi4vdxm67HtC4iEs/Do593cb/+9rPk19+H3Z9fh3mWdtie181fzuO6vPi3dZOH6zzsx7BMt+fvD7t9nYftfB0WP5/17ouXcvPXYdJn67QcUvxvgATDNkq0D/PLKrEmcZ29hE+33UvzuE2XL/67OL0t8zpc/CbJ43AcfnuTTF8ew7Tos2bSYduh6e2rYtPU11e/nZf14qVA9KcBC3+TctRVnJfbOPvbLp+Gy2U6phd/3rza9mO7YZgk+2hBpBm3t/Vpvk2X8/P0FdHHt3FeF83wddABZ//12EyN0i2jjhD5W+yfw+Wkf3ZvCIiQiIhYTmAi9jPlxIyOnEMKoiQqoiYaoiU6ogdw5wLVOQmASAKkkzCrlFllTEcDIZ2CKImKqImG0ECaMQOdNNC3lbqTwtSOagd1osgliBTmdHQ6Oh2dDk7AnI7OgM6AzoDOgM5AZUGqeTfidFE6zyb0rK3gIdZkcKglboJcL3Fyv3/8qf//0lKEdId0h3BjTJ7Ac7/f34NCfxaRhjzB0NHfc+8sTMQwSF5zinQvNeeI3ojeCF680xJXCi1xrajUkJ8wtaOvZy/olBgtgaLF1MbUxtTG1MbQNoo8TRDs/n7/V7O2q21CeH83QaFXLXqrs5uTMLGYQWMEtV2TE3AiHBEQIRERmlmnkW1JCfYUsD1N6EzgtO5CW6dwAo4IiJCIiBiWFHsKVBJgidYLKWUZZRllGWUZZRllGWUZv6YMMqAiaqIhWqIjerRajrr3Wivt2wfrAtco4lR1WketJa7KD6uws08nZ5ScUXJGyRklZ5ScUXJGKRCltB2xjdHpQEXUREO0REfY9BLTa4VmV1hL6dSS5SvZFSWqg+83JwqsQ9+m4f2f+wdWgF7TteCzqTSkNm+psbTnSo1l7dbzjFFdiwHWMxq2Yli0sA6qGK9ivIrxKsQrLSn1VvAC5q3oreH9QRHCUqMSWHliXwMemLamtqa2hjaWmtqa2pramtoG2h8FTe5wqtj0htMbTA/0XYV5Dc+chmdOwz5raGn5Ubf8+locOJ+wEfwViZ1Upm6pbqEOFbppPykwRS89T2LBu44yJGswS0dLR0vH9XU4zjCv5wluZ2Bvh5/CEQEREhEREwmRomV6RugZgZ2tKDmkImqiIVqiIzT+f1btup4="); 48 | } else if (wave >= 15) { 49 | return Schematics.readBase64("bXNjaAF4nE2UYY6bMBSEX2yMDYZADpJ/vcmewCXeKhILEUl2lZ6wZ6q2Td94tFITKR/BzIzfs40c5GCkWtJblvElT7d1O//Mp5dL+li+STzl67SdL7fzuoiMMlzmdL2l5Xx/O36keZYxbW/rlk/HaV3e82PdpJ7TMuVNui1f0nk7XtbzchP/Pd1ueXuITdsk8brOSYfSkmepXu/XLIf/bh316kcWd03z+yrtZf3I23FZT1nCfZnXdFL7+rpuaigRGa8J835IN+lcjst9mvP9Kn56TPO6ZBH5JeVj8LPTb7kusERFONmBtZS/nggUN3ykJSLRET2xJwZiBHBlDEORZcUgqyKcGIzUnJUnQpkH8mpFS0SiI3piTwwMGr9KNLXCYsygMKAUZhhkGGQQpPNAEKBBXlUdyjdqXVwGeo5slV7aUoWOWUQFNahQg6W1pbWFdaNooLI0szSzNKtgVpW+6xjyS1dqwhOlExXlFeUV5Y5yR7mj3FHuKHeQt4omuFKSOj7/PD+fn7B5fpqoY63DMnd6FbGKtfS7skXgOTBo5H9BB9HtAkNYoiJ0NnsNDdgdNQxr/RaXmi6eLp4uni6eLp4uHi5ogC44Cilr6ykPlAfKA+WB8kB54EoHlVugI3piTwzEiBVtWCQ2967XdtlSv2n1jgtGe4WlMQbbsVg2tGxo2dCyoWVDy5YVl4MSywqUbVzkLeUt5S3lLeUt5ZHycsAqwSLpjzbUwRk3yupG1A80RCsGEVHn/Pv5F/M28CjnJfK8RJ6XyJ5GbqSOaeUclz7h+PbcSB0zOmZ0yMAjHbZch6kDAyQdzXqa9TyPPeU95Wiu7tgeOqsYsGd76vY8GHh9GJzqQDREixfIHqcAT458w5RVG7j9YbWry0wKKsIRNeFhNtB6oPXAKQ1YGqDDzFC9QcLId5h8vcp2gCEsURGOqAmPDTgyaGTQiCCMRTRrRBDQE3tiIDTxH8yBaxc="); 50 | } else if (wave >= 10) { 51 | return Schematics.readBase64("bXNjaAF4nE2S/27aMBSFr38RO3bC+iD8tUfpE3jBXSOlCUpCEXv0dRq716eVBkIfhvMd7IvpSEdNds5vhb49l2Ff1vFXOT9f8m3+TvFctmEdL/u4zERHetrHPc/j9e00LPN7uS8rpf2VFf7klqeJTF4Hitsy5fV0yXOZ6Om/xYnf/SzUbEPe97JS86PyTmktlzxyahnnnfx1npZ85oB9zeNEUb56ybI3jg7LWk7zdZjKdaP2stzKepqXcyG35el9Ifty3QoR3flFjurjADREiuFJCQLQAhFIQAf0kldoUWhR0qIZnsgwApHldcSHnQQUeyRrBxyARrajxTswalIjaZA0SBpJNowaMYhYRCwiViIHhpfTWApOfiDwu1YO5Cgpwk4sdFeXDAVowACWVOTGRsqcVMiR+6/BKYECNGAA9hKj9e7x8fgrT9KP32QfH/z6UyfWf85dhAYtDVoatDTSImh5zYhAAjqgJ1MHLsPxMreOoWULvk6fUXUP3UP30D30AD2I7mVWDhsLUAPUADVADVBbqK2o/dcOZcwcbungNR/W1uPL9OV8DA8E0kpuhxREKRA42XrEfxkhRAgRQoKQICQICUKCkCAkCB2ETm6MQAMGsAD/4XLxakuHls9BdmjpccPl2iuBBgxgAQfInWDUlh4tvbT8Az+/YvE="); 52 | } else if (wave >= 5) { 53 | return Schematics.readBase64("bXNjaAF4nD2RfY6bMBDFn23AYBOpe5D806vkBC5xJSTWRoZslJ591XQ+pCKhX2LeezOewQUXi66kz4wft7ycta1/8v22p2f5iXjPx9LW/VxrAWZ8nOuZyvr4vC61fOVXbbj8P3qmbUN/pO2rIh51S+26p5I3zC3vaaV/dS0nXGoLwl6fuV1LvWeMj7LVdM8N81JbvpbHsuXHAf8rnWduL/hjkV+InPI7cZMvADd60UOeATAED8MYFZMiKKJiZqFRn2GfJXg4R5iAjhDgekKENYSZlVYNlg2OMCkCLCNylFWlU6Vj5UDwI833/f3+S2lUBiN99lTNUYZlBIVkOM4YySdlOpV0KulU0mmZXtoH9ymwCrrFRJjgGHKRXi/Sq29Q36C+QX3cp4B8jKCIihmOJyQD9dwkw8INPDa6HXnf3+gMj18CvAZ4DfAaMEJWMXKAJzg+nCCLCeR+02O4BPcklSxvQkYZuDuZlIaIJKgk8hHPxvAOo0qiSqJKZt01b94wrMIpOgVPkiC+mX3/ABd1TCk="); 54 | } 55 | 56 | return Schematics.readBase64("bXNjaAF4nCWQUW7DIBBExwu2id2PXiQflXqTnIBipFpywcJOo/ToVdR0B1tCD+F9uwMYMQps8l8Rr5cY9lzmnzhdVn9LbxinuIUyr/ucEzDChZy+4z0XjCGvayznm18W9B9+32O5Y9zy4st59SkuGNZ804qUpwj76ecFLyGXeE7XsMTrBndNS/ZTLHgpcfWzenlOO/ot1HYwvgQA77pgUb8WaBQdDNGjIdyB04GBFc0hNBREoYJR9AChAlGFRgWwyB5oOUvQOV3PP9jnQ02h2SocG4ia6gnNTo9bJjFwSmNa3dXfhr+NNnOcbfXQMNVAdDrK6ACWGYVAajiI46VgeoXwssK0en5iaMZ2DMCm1snzofl+IcI3YDxFx1YnKgNHQhiHN1K0B2oJb9009bUq5IA5YHWjqMJA4R8Izz5a"); 57 | } else if (State.planet.equals(Planets.erekir.name)) { 58 | if (wave >= 25) { 59 | return Schematics.readBase64("bXNjaAF4nE2QQW6DMBBFxzYkQCCLLnoLNt30BD1BllUXjpm0Vl2IDChST9/5nk2FrDfj/+fbmM40OKpm/8P0fOGwLTn+8nS5+8f8lvk75lc6TbyGHO9bXGYiOiR/5bSSff9oqJr2sNGw7fPnuvE8PnxK1Aefr3Fi7Q7XzD580XGKt9u+Mp33eeJ8S8tjLNPnsGQefcjLfUlxpaf/42Py+ZPl2BdZ1BAZQUvOCjoywEnRA1AtYLSThdpBMFQhwyDFidIhxcic1AiwVdlGpU4LJ7RicZitBaIB5S5ONKvB5kD4RKsQTYKTogfqcrTAKCzCar1ZowONDrRqadXSwnIUdJjvoDj11dLV+NsOXot3oPIcZbPXrtduQAdYvMuATCOosD3A8gdiTTjq"); 60 | } else if (wave >= 20) { 61 | return Schematics.readBase64("bXNjaAF4nCVPS27FIBAzn5c232Vv0GW2vUFP8JZVF4TQFj0KEUkUqafvDCAxHmOPNWDCoKCj+XV4uTt7pOz/3HrfzBXfs3v4/IZ+dbvNfjt8igCaYBYXdsiPzwZ6Pe2B8Tjj9364OF8mBAzW5MWvrrJmyc7YH0xnXF3+Cumay9BkU3azsTltKfidkl/p4gkQBM9QDC2UJOggGHqWBFsUZNVEfZR1ThKToqiCQUFoKkVTVdOs3dDgBuo1p3DTVegZWpQdWiqCDSWsZS7IB9SoOqU5s6N1i1YsPcrpWZMYKhsqG7lnKB8Za/TIe4KALP/HFy5k"); 62 | } else if (wave >= 15) { 63 | return Schematics.readBase64("bXNjaAF4nCWOS27EIBBEC/B4xt9lbpCll3OEnGCWURYMJokVAha2ZSmnTzdtZB7dVV2AEb1BFe2vx8vDuz3l5c/Pj9We8S37nyXf0c1+c3lZ9yVFAHWwTx826PePC6r5cDuG/Yhf2+7jdNoQUD+zt+4b4xFnnz9DOqdiG13KfrIupzWFZaOsV/pxBRThBsNoBK2gg2aVLJpW0ZQ0tcxprhQMHRTDQPFWNCNaxVqFGhdQq+IUEFpBx2hQ3tDQRuONhDVcK/IBcg2PtxzHzZtoxdKhfJ1ovVS9VANK9sDRDC0wfN/Aln+r5Cmn"); 64 | } else if (wave >= 10) { 65 | return Schematics.readBase64("bXNjaAF4nCWOTW6EMAyFX0IGyu+yN+iSVc/QE8yy6iJAaKPJJCiAUHv62gkS+Ww/288Y0BVQXj8NXu9mPkK0f2a5b/ryH9E8bHxHu5h9jnY7bPAASqcn43bIz68KajnnA8Nk4q9z9nyOl3YO/XH67/0wPqflFI2efzCcfjFxdeEa89gcohn1HMMWnN1RLXZdz92QyRv9qABBeEHBqFFIQpPRQrJKLQVFSRO5KPOcpIzAqmRQoOipcjFpirUbStxAseItHDQZLaNGuqGmR3ADuQhCzdWGbyyTqeApxUYNnZu01NIifS1rEl3Oupz1HDME7+zz6p5Mya/nln/qZjJh"); 66 | } else if (wave >= 5) { 67 | return Schematics.readBase64("bXNjaAF4nFWOQU7EMAxFf9PQmbZiNkhIHKIb7sAJZolYpKkZIjJJ5aaq4PQ4yQop0rP/97eDEWMLHcyd8HwlmyK7X1quqznCG9O341eMC22W3ZpcDAA6b2byG9T7xwl62W3CZSb+8d7t9+kw3uMx7eG2JQq17WYmY79w2cNC/OnjMdWYjUyTsRzX6N2Gp3+xyRu+kRx8Qb4KNIIT2owzWiXoKwao7MpIK1XxmiqqmlPSCdpcNmVIaUFXxeLp7D2IpCFP5y256CuGjLOMNJ1IyIFe+rK43B7kZwUi/gEXKDYp"); 68 | } 69 | 70 | return Schematics.readBase64("bXNjaAF4nFWOPU7FMBCEx47Jy4/0KkTBHVJwCE7wSkThOAtY+MXRJlEEp2c3rpCLzzuz4zF69BXc7O+EpxuFLXP8pem2+GN+ZfqO/IJ+ojVwXLaYZwB18iOlFfbtvYab9rDhOhL/pBT3+3D4lFCPTD584brPE/FHysdQ9kJmGnzgvOQUVzz+zw3J8ydJxTO0BzCCC4yiKWgLOrWMrlg5DSojU6cpW3JWJ4NKrwoLUwnqIp6eU8+J5CCS01cgaAs6RYPzD9IN8yDOObYqa5stuBSI+AccXTEa"); 71 | } 72 | 73 | return null; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/sectorized/constant/State.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import sectorized.faction.core.Member; 4 | 5 | public class State { 6 | public static GameState gameState = GameState.INACTIVE; 7 | public static double time = 0; 8 | public static String planet; 9 | public static Member winner = null; 10 | 11 | public enum GameState { 12 | ACTIVE, 13 | INACTIVE, 14 | GAMEOVER, 15 | LOCKED, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/sectorized/constant/Units.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import mindustry.type.UnitType; 4 | 5 | import static mindustry.content.UnitTypes.*; 6 | 7 | public class Units { 8 | public static final UnitType[] unitTypes = new UnitType[]{ 9 | dagger, 10 | mace, 11 | fortress, 12 | scepter, 13 | reign, 14 | 15 | nova, 16 | pulsar, 17 | quasar, 18 | vela, 19 | corvus, 20 | 21 | crawler, 22 | atrax, 23 | spiroct, 24 | arkyid, 25 | toxopid, 26 | 27 | flare, 28 | eclipse, 29 | horizon, 30 | zenith, 31 | antumbra, 32 | 33 | mono, 34 | poly, 35 | mega, 36 | quad, 37 | oct, 38 | 39 | risso, 40 | minke, 41 | bryde, 42 | sei, 43 | omura, 44 | 45 | retusa, 46 | oxynoe, 47 | cyerce, 48 | aegires, 49 | navanax, 50 | 51 | alpha, 52 | beta, 53 | gamma, 54 | 55 | stell, 56 | locus, 57 | precept, 58 | vanquish, 59 | conquer, 60 | 61 | merui, 62 | cleroi, 63 | anthicus, 64 | tecta, 65 | collaris, 66 | 67 | elude, 68 | avert, 69 | obviate, 70 | quell, 71 | disrupt, 72 | 73 | evoke, 74 | incite, 75 | emanate 76 | }; 77 | 78 | public static float healthMultiplier = 1.0f; 79 | 80 | static { 81 | for (UnitType unitType : unitTypes) { 82 | unitType.payloadCapacity = 0; 83 | } 84 | 85 | zenith.speed *= 0.6f; 86 | zenith.health *= 0.5f; 87 | 88 | mega.speed *= 0.5f; 89 | mega.health *= 0.8f; 90 | 91 | antumbra.speed *= 0.8f; 92 | antumbra.health *= 0.8f; 93 | 94 | quad.speed *= 0.8f; 95 | 96 | eclipse.speed *= 0.7f; 97 | eclipse.health *= 0.7f; 98 | 99 | crawler.speed *= 1.25f; 100 | dagger.speed *= 1.25f; 101 | nova.speed *= 1.25f; 102 | 103 | atrax.speed *= 1.25f; 104 | mace.speed *= 1.25f; 105 | pulsar.speed *= 1.25f; 106 | 107 | spiroct.speed *= 1.25f; 108 | fortress.speed *= 1.25f; 109 | quad.speed *= 1.25f; 110 | 111 | arkyid.speed *= 1.25f; 112 | scepter.speed *= 1.25f; 113 | vela.speed *= 1.25f; 114 | 115 | toxopid.speed *= 1.25f; 116 | reign.speed *= 1.25f; 117 | corvus.speed *= 1.25f; 118 | 119 | emanate.speed *= 0.6f; 120 | } 121 | 122 | public static void setUnitHealthMultiplier(float multiplier) { 123 | for (UnitType unitType : unitTypes) { 124 | unitType.health = unitType.health / healthMultiplier * multiplier; 125 | } 126 | 127 | healthMultiplier = multiplier; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/sectorized/constant/VaultLogic.java: -------------------------------------------------------------------------------- 1 | package sectorized.constant; 2 | 3 | import arc.math.geom.Point2; 4 | import mindustry.world.Block; 5 | import mindustry.world.Edges; 6 | import mindustry.world.Tile; 7 | import mindustry.world.blocks.storage.CoreBlock; 8 | 9 | import static mindustry.Vars.world; 10 | 11 | public class VaultLogic { 12 | public static boolean adjacentToCore(int x, int y, Block block) { 13 | Point2[] nearby = Edges.getEdges(block.size); 14 | 15 | for (Point2 point2 : nearby) { 16 | Tile neighbor = world.tile(x + point2.x, y + point2.y); 17 | 18 | if (neighbor.block() instanceof CoreBlock) return true; 19 | } 20 | return false; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/sectorized/faction/core/Faction.java: -------------------------------------------------------------------------------- 1 | package sectorized.faction.core; 2 | 3 | import arc.struct.Seq; 4 | import mindustry.game.Team; 5 | import sectorized.constant.MessageUtils; 6 | 7 | public class Faction { 8 | public final Team team; 9 | 10 | public int maxCores = 0; 11 | 12 | public final Seq members = new Seq<>(); 13 | 14 | public final double time; 15 | 16 | public Team lastAttacker; 17 | 18 | public Faction(Team team, double time) { 19 | this.team = team; 20 | this.time = time; 21 | } 22 | 23 | public void addMember(Member member) { 24 | this.members.add(member); 25 | member.faction = this; 26 | member.state = Member.MemberState.ALIVE; 27 | member.player.team(team); 28 | } 29 | 30 | public void removeMember(Member member) { 31 | if (this.members.first() == member) { 32 | if (this.members.size > 1) { 33 | Member newLeader = this.members.get(1); 34 | 35 | MessageUtils.sendMessage(newLeader.player, "You are now the team leader because " + MessageUtils.cPlayer + member.player.name + MessageUtils.cDefault + " left the team!", MessageUtils.MessageLevel.INFO); 36 | } 37 | } 38 | 39 | this.members.remove(member); 40 | member.faction = null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/sectorized/faction/core/Member.java: -------------------------------------------------------------------------------- 1 | package sectorized.faction.core; 2 | 3 | import mindustry.gen.Player; 4 | 5 | public class Member { 6 | public Player player; 7 | public Faction faction; 8 | public boolean online = false; 9 | public MemberState state = MemberState.WAITING; 10 | 11 | public int wins = 0, losses = 0, score = 0, rank = -1; 12 | public float ratio = 0; 13 | public String discordTag; 14 | 15 | public Member(Player player) { 16 | this.player = player; 17 | } 18 | 19 | public enum MemberState { 20 | ALIVE, 21 | ELIMINATED, 22 | WAITING 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/sectorized/faction/logic/FactionLogic.java: -------------------------------------------------------------------------------- 1 | package sectorized.faction.logic; 2 | 3 | import arc.Events; 4 | import arc.struct.Seq; 5 | import arc.util.Strings; 6 | import arc.util.Time; 7 | import arc.util.Timer; 8 | import mindustry.Vars; 9 | import mindustry.ai.types.FlyingAI; 10 | import mindustry.ai.types.GroundAI; 11 | import mindustry.core.GameState; 12 | import mindustry.game.EventType; 13 | import mindustry.game.Team; 14 | import mindustry.gen.Groups; 15 | import mindustry.gen.Unit; 16 | import mindustry.world.blocks.storage.CoreBlock; 17 | import sectorized.SectorizedEvents; 18 | import sectorized.constant.DiscordBot; 19 | import sectorized.constant.MessageUtils; 20 | import sectorized.constant.State; 21 | import sectorized.faction.core.Faction; 22 | import sectorized.faction.core.Member; 23 | import sectorized.faction.persistence.RankingPersistence; 24 | 25 | import static mindustry.Vars.state; 26 | 27 | public class FactionLogic { 28 | private final RankingPersistence persistence; 29 | 30 | private final Seq factions = new Seq<>(); 31 | 32 | private final Seq available = new Seq<>(); 33 | 34 | public FactionLogic(RankingPersistence persistence) { 35 | this.persistence = persistence; 36 | 37 | available.addAll(Team.all); 38 | available.remove(Team.derelict); 39 | available.remove(Team.sharded); 40 | available.remove(Team.crux); 41 | available.remove(Team.malis); 42 | available.shuffle(); 43 | 44 | Team.crux.data().unitCap = Integer.MAX_VALUE; 45 | 46 | Events.run(EventType.Trigger.update, () -> { 47 | if (state.rules.waves && state.rules.waveTimer) { 48 | if (!Vars.logic.isWaitingWave()) { 49 | state.wavetime = Math.max(state.wavetime - Time.delta, 0); 50 | } 51 | } 52 | }); 53 | 54 | Events.on(EventType.UnitSpawnEvent.class, event -> { 55 | event.unit.controller(!event.unit.isFlying() ? new GroundAI() : new FlyingAI()); 56 | }); 57 | 58 | for (Team team : available) { 59 | team.rules().aiCoreSpawn = false; 60 | team.rules().rtsAi = false; 61 | team.rules().rtsMinSquad = Integer.MAX_VALUE; 62 | team.rules().rtsMinWeight = Float.MAX_VALUE; 63 | } 64 | } 65 | 66 | public Faction getFaction(Team team) { 67 | Faction faction = factions.find(f -> f.team == team); 68 | 69 | return faction; 70 | } 71 | 72 | public Faction getNewFaction() { 73 | Faction faction = new Faction(available.pop(), State.time); 74 | 75 | factions.add(faction); 76 | state.set(GameState.State.playing); 77 | 78 | return faction; 79 | } 80 | 81 | public void destroyCores(Faction faction) { 82 | for (CoreBlock.CoreBuild core : faction.team.cores().copy()) { 83 | core.kill(); 84 | } 85 | } 86 | 87 | public void changeFaction(Faction oldFaction, Faction newFaction, Member member) { 88 | oldFaction.removeMember(member); 89 | newFaction.addMember(member); 90 | 91 | member.player.unit().kill(); 92 | } 93 | 94 | public void addToFaction(Faction newFaction, Member member) { 95 | newFaction.addMember(member); 96 | 97 | member.player.unit().kill(); 98 | } 99 | 100 | public void removeFaction(Faction defender, Faction attacker, boolean fallback) { 101 | factions.remove(defender); 102 | if (factions.size == 0) state.set(GameState.State.paused); 103 | available.insert(0, defender.team); 104 | 105 | defender.members.each(m -> { 106 | if (m.player.unit() != null) m.player.unit().kill(); 107 | }); 108 | 109 | double timeSinceSpawned = State.time - defender.time; 110 | if (defender.maxCores <= 3 && timeSinceSpawned < 60 * 60 * 5) { 111 | defender.members.each(m -> { 112 | MessageUtils.sendMessage(m.player, "No points lost because you spawned less than " + MessageUtils.cInfo + "5 minutes" + MessageUtils.cDefault + " ago", MessageUtils.MessageLevel.INFO); 113 | }); 114 | 115 | if (attacker != null) { 116 | attacker.members.each(m -> { 117 | MessageUtils.sendMessage(m.player, "No points gained because the faction you killed spawned less than " + MessageUtils.cInfo + "5 minutes" + MessageUtils.cDefault + " ago", MessageUtils.MessageLevel.INFO); 118 | }); 119 | } 120 | } else { 121 | if (attacker == null) persistence.calculateNewRankings(defender); 122 | else persistence.calculateNewRankings(attacker, defender); 123 | } 124 | 125 | Groups.unit.each(u -> u.team == defender.team, Unit::kill); 126 | 127 | if (defender.members.size > 0) { 128 | if (attacker == null) { 129 | MessageUtils.sendMessage(MessageUtils.cPlayer + defender.members.first().player.name + MessageUtils.cDefault + " got eliminated!", MessageUtils.MessageLevel.ELIMINATION); 130 | } else if (fallback) { 131 | MessageUtils.sendMessage(MessageUtils.cPlayer + defender.members.first().player.name + MessageUtils.cDefault + " got eliminated! Points awarded to " + MessageUtils.cDanger + attacker.members.first().player.name + MessageUtils.cDefault + "!", MessageUtils.MessageLevel.ELIMINATION); 132 | } else { 133 | MessageUtils.sendMessage(MessageUtils.cPlayer + defender.members.first().player.name + MessageUtils.cDefault + " got eliminated by " + MessageUtils.cDanger + attacker.members.first().player.name + MessageUtils.cDefault + "!", MessageUtils.MessageLevel.ELIMINATION); 134 | } 135 | } 136 | 137 | defender.members.each(m -> { 138 | if (m.faction == defender) { 139 | m.state = Member.MemberState.ELIMINATED; 140 | m.faction = null; 141 | m.player.team(Team.derelict); 142 | m.player.unit().kill(); 143 | 144 | Timer.schedule(() -> { 145 | if (m.faction == null) { 146 | m.state = Member.MemberState.WAITING; 147 | 148 | if (m.online) { 149 | MessageUtils.sendMessage(m.player, MessageUtils.cInfo + "5 minutes" + MessageUtils.cDefault + " have passed, you can reconnect to spawn again!", MessageUtils.MessageLevel.INFO); 150 | } 151 | } 152 | }, 60 * 5); 153 | } 154 | }); 155 | 156 | if (Vars.state.wave < 5) return; 157 | 158 | if (factions.size == 1 && !(defender.maxCores <= 3 && timeSinceSpawned < 60 * 60 * 5) && State.gameState != State.GameState.GAMEOVER) { 159 | Member winner = factions.first().members.first(); 160 | State.winner = winner; 161 | winner.wins++; 162 | persistence.setRanking(winner); 163 | 164 | DiscordBot.sendMessage("**Game Over!** Player *" + Strings.stripColors(winner.player.name).substring(1).replace("@", "at") + "* won the game in " + Vars.state.wave + " waves."); 165 | 166 | Events.fire(new SectorizedEvents.RestartEvent("Game over! " + MessageUtils.cPlayer + winner.player.name + MessageUtils.cDefault + " won")); 167 | } else if (factions.size == 0 && State.gameState != State.GameState.GAMEOVER) { 168 | DiscordBot.sendMessage("**Game Over!** Crux won the game in " + Vars.state.wave + " waves."); 169 | 170 | Events.fire(new SectorizedEvents.RestartEvent("Game over! " + MessageUtils.cDanger + "crux" + MessageUtils.cDefault + " won.")); 171 | } 172 | } 173 | 174 | public Seq getJoinableFactionLeaders(Member member) { 175 | Seq leaders = new Seq<>(); 176 | 177 | factions.each(f -> !f.members.contains(member), f -> { 178 | leaders.add(f.members.first()); 179 | }); 180 | 181 | return leaders; 182 | } 183 | 184 | public boolean isFactionLeaders(Member possibleLeader, Member requester) { 185 | return factions.contains(f -> f.members.first().equals(possibleLeader) && !f.members.first().equals(requester)); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/sectorized/faction/logic/MemberLogic.java: -------------------------------------------------------------------------------- 1 | package sectorized.faction.logic; 2 | 3 | import arc.struct.Seq; 4 | import mindustry.game.Team; 5 | import mindustry.gen.Player; 6 | import sectorized.constant.DiscordBot; 7 | import sectorized.constant.MessageUtils; 8 | import sectorized.faction.core.Member; 9 | import sectorized.faction.persistence.RankingPersistence; 10 | 11 | public class MemberLogic { 12 | private final RankingPersistence persistence; 13 | 14 | private final Seq members = new Seq<>(); 15 | 16 | public MemberLogic(RankingPersistence persistence) { 17 | this.persistence = persistence; 18 | } 19 | 20 | public Member playerJoin(Player player) { 21 | Member member = members.find(m -> m.player.uuid().equals(player.uuid())); 22 | 23 | if (member == null) { 24 | member = addMember(player); 25 | } else { 26 | member.player = player; 27 | } 28 | 29 | persistence.getRanking(member); 30 | 31 | member.online = true; 32 | 33 | if (member.faction == null) { 34 | member.player.team(Team.derelict); 35 | member.player.unit().kill(); 36 | } else { 37 | member.player.team(member.faction.team); 38 | } 39 | 40 | if (member.discordTag == null) { 41 | MessageUtils.sendMessage(member.player, "Link your account to discord to display your rank using [purple]/register username#0000[white]!", MessageUtils.MessageLevel.INFO); 42 | } 43 | 44 | DiscordBot.assignRole(member); 45 | 46 | return member; 47 | } 48 | 49 | public Member playerLeave(Player player) { 50 | Member member = getMember(player); 51 | 52 | persistence.setRanking(member); 53 | member.online = false; 54 | 55 | return member; 56 | } 57 | 58 | public Member getMember(Player player) { 59 | Member member = members.find(m -> m.player.uuid().equals(player.uuid())); 60 | 61 | assert member != null; 62 | 63 | return member; 64 | } 65 | 66 | private Member addMember(Player player) { 67 | Member member = new Member(player); 68 | members.add(member); 69 | 70 | return member; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/sectorized/faction/persistence/RankingPersistence.java: -------------------------------------------------------------------------------- 1 | package sectorized.faction.persistence; 2 | 3 | import arc.Core; 4 | import arc.struct.Seq; 5 | import arc.util.Log; 6 | import arc.util.Strings; 7 | import com.google.gson.Gson; 8 | import sectorized.constant.Config; 9 | import sectorized.constant.DiscordBot; 10 | import sectorized.constant.MessageUtils; 11 | import sectorized.constant.RankIcons; 12 | import sectorized.faction.core.Faction; 13 | import sectorized.faction.core.Member; 14 | 15 | import java.io.IOException; 16 | import java.io.Reader; 17 | import java.nio.file.Files; 18 | import java.nio.file.Paths; 19 | import java.sql.*; 20 | import java.time.ZoneOffset; 21 | import java.time.ZonedDateTime; 22 | import java.time.format.DateTimeFormatter; 23 | import java.util.Calendar; 24 | import java.util.Comparator; 25 | import java.util.Date; 26 | import java.util.Iterator; 27 | 28 | public class RankingPersistence { 29 | private final Seq leaderboard = new Seq<>(); 30 | private final double k = 10; 31 | private final double offset = 10; 32 | private Connection connection = null; 33 | 34 | public final String[] leaderboardTexts = new String[10]; 35 | public int leaderBoardPages = 0; 36 | 37 | public RankingPersistence() { 38 | if (Config.c.databaseEnabled) { 39 | try { 40 | Class.forName("org.mariadb.jdbc.Driver"); 41 | 42 | DBUrl dbUrl = readConfig(); 43 | 44 | connection = DriverManager.getConnection(dbUrl.url, dbUrl.user, dbUrl.password); 45 | 46 | updateScoreDecay(); 47 | getLeaderboard(); 48 | } catch (SQLException | IOException | ClassNotFoundException e) { 49 | Log.err(e.getMessage()); 50 | e.printStackTrace(); 51 | } 52 | } 53 | } 54 | 55 | private void updateScoreDecay() { 56 | if (Config.c.updateScoreDecay) { 57 | Calendar calendar = Calendar.getInstance(); 58 | calendar.set(Calendar.HOUR_OF_DAY, 0); 59 | calendar.set(Calendar.MINUTE, 0); 60 | calendar.set(Calendar.SECOND, 0); 61 | calendar.set(Calendar.MILLISECOND, 0); 62 | Date today = calendar.getTime(); 63 | calendar.add(Calendar.DATE, -1); 64 | Date yesterday = calendar.getTime(); 65 | 66 | Date lastScoreDecayDate = new Date((long) Core.settings.get("lastScoreDecayDate", yesterday.getTime())); 67 | 68 | Core.settings.put("lastScoreDecayDate", today.getTime()); 69 | 70 | if (lastScoreDecayDate.before(today)) { 71 | try { 72 | PreparedStatement statement = connection.prepareStatement("UPDATE ranking SET score = score * 0.99 WHERE score > 100"); 73 | statement.executeQuery(); 74 | } catch (SQLException e) { 75 | e.printStackTrace(); 76 | } 77 | } 78 | } 79 | } 80 | 81 | public void updateHallfOfFame() { 82 | if (Config.c.databaseEnabled) { 83 | Calendar calendar = Calendar.getInstance(); 84 | calendar.set(Calendar.HOUR_OF_DAY, 0); 85 | calendar.set(Calendar.MINUTE, 0); 86 | calendar.set(Calendar.SECOND, 0); 87 | calendar.set(Calendar.MILLISECOND, 0); 88 | Date today = calendar.getTime(); 89 | calendar.add(Calendar.DATE, -6); 90 | Date sixDaysAgo = calendar.getTime(); 91 | calendar.add(Calendar.DATE, -1); 92 | Date sevenDaysAgo = calendar.getTime(); 93 | 94 | Date lastHallOfFameDate = new Date((long) Core.settings.get("lastHallOfFameDate", sevenDaysAgo.getTime())); 95 | 96 | String zone = ZoneOffset.systemDefault().toString(); 97 | String currentDate = DateTimeFormatter.ofPattern("uuuu/MM/dd - HH:mm").format(ZonedDateTime.now()); 98 | 99 | StringBuilder text = new StringBuilder(":trophy: :regional_indicator_l: :regional_indicator_e: :regional_indicator_a: :regional_indicator_d: :regional_indicator_e: :regional_indicator_r: :regional_indicator_b: :regional_indicator_o: :regional_indicator_a: :regional_indicator_r: :regional_indicator_d: :trophy:\n"); 100 | int i = 0; 101 | for (LeaderBoardEntry entry : leaderboard) { 102 | text.append("\n").append(entry.rank).append(" - "); 103 | 104 | String medal; 105 | switch (entry.rank) { 106 | case 1: 107 | medal = ":first_place:"; 108 | break; 109 | case 2: 110 | medal = ":second_place:"; 111 | break; 112 | case 3: 113 | medal = ":third_place:"; 114 | break; 115 | default: 116 | medal = ":medal:"; 117 | } 118 | 119 | text.append(medal) 120 | .append("**") 121 | .append(entry.name) 122 | .append("**") 123 | .append(" - Score: ") 124 | .append(entry.score) 125 | .append(" - Wins: ") 126 | .append(entry.wins) 127 | .append(" - Losses: ") 128 | .append(entry.losses); 129 | 130 | i++; 131 | if (i == 10) break; 132 | } 133 | text.append("\n\n:date: *").append(currentDate).append(" - ").append(zone).append("* :clock3:"); 134 | 135 | if (lastHallOfFameDate.before(sixDaysAgo)) { 136 | Core.settings.put("lastHallOfFameDate", today.getTime()); 137 | 138 | DiscordBot.sendMessageToHallOfFame(text.toString()); 139 | } else { 140 | DiscordBot.editLastMessageInHallOfFame(text.toString()); 141 | } 142 | } 143 | } 144 | 145 | public void closeConnection() { 146 | if (Config.c.databaseEnabled) { 147 | try { 148 | if (connection != null) connection.close(); 149 | } catch (SQLException e) { 150 | e.printStackTrace(); 151 | } 152 | } 153 | } 154 | 155 | public void getRanking(Member member) { 156 | if (Config.c.databaseEnabled) { 157 | member.player.name = "." + member.player.name; 158 | 159 | if (connection != null) { 160 | try { 161 | PreparedStatement statement = connection.prepareStatement("SELECT * FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY empty ORDER BY score DESC) AS rank FROM ranking) t WHERE uuid = ?"); 162 | statement.setString(1, member.player.uuid()); 163 | 164 | ResultSet resultSet = statement.executeQuery(); 165 | 166 | if (resultSet.next()) { 167 | member.score = resultSet.getInt("score"); 168 | member.wins = resultSet.getInt("wins"); 169 | member.losses = resultSet.getInt("losses"); 170 | member.rank = member.score > 0 ? resultSet.getInt("rank") : -1; 171 | member.discordTag = resultSet.getString("discordTag"); 172 | 173 | member.ratio = (float) member.wins / Math.max((float) member.losses, 1); 174 | } else { 175 | setRanking(member); 176 | } 177 | } catch (SQLException e) { 178 | e.printStackTrace(); 179 | } 180 | } 181 | 182 | member.player.name = RankIcons.getRankIcon(member.rank) + member.player.name.substring(1); 183 | } 184 | } 185 | 186 | private void getLeaderboard() { 187 | if (Config.c.databaseEnabled) { 188 | if (connection != null) { 189 | try { 190 | PreparedStatement statement = connection.prepareStatement("SELECT *, ROW_NUMBER() OVER(PARTITION BY empty ORDER BY score DESC) AS rank FROM ranking WHERE score > 0 LIMIT 100"); 191 | 192 | ResultSet rs = statement.executeQuery(); 193 | 194 | while (rs.next()) { 195 | LeaderBoardEntry leaderBoardEntry = new LeaderBoardEntry(rs.getString("name"), rs.getInt("rank"), rs.getInt("score"), rs.getInt("wins"), rs.getInt("losses")); 196 | 197 | leaderboard.add(leaderBoardEntry); 198 | } 199 | } catch (SQLException e) { 200 | e.printStackTrace(); 201 | } 202 | } 203 | 204 | Iterator it = leaderboard.iterator(); 205 | 206 | int elements = 0; 207 | StringBuilder text = new StringBuilder(); 208 | 209 | while (it.hasNext()) { 210 | RankingPersistence.LeaderBoardEntry entry = it.next(); 211 | 212 | text.append(entry.rank) 213 | .append(". [white]") 214 | .append(entry.name) 215 | .append(MessageUtils.cDefault + ", Score: " + MessageUtils.cHighlight2) 216 | .append(entry.score) 217 | .append(MessageUtils.cDefault + ", Wins: " + MessageUtils.cHighlight3) 218 | .append(entry.wins) 219 | .append(MessageUtils.cDefault + ", Losses: " + MessageUtils.cDanger) 220 | .append(entry.losses) 221 | .append(MessageUtils.cDefault) 222 | .append("\n"); 223 | 224 | elements++; 225 | 226 | if (elements == 10 || !it.hasNext()) { 227 | leaderboardTexts[leaderBoardPages] = text.toString(); 228 | text = new StringBuilder(); 229 | leaderBoardPages++; 230 | elements = 0; 231 | } 232 | } 233 | } 234 | } 235 | 236 | public void setRanking(Member member) { 237 | if (Config.c.databaseEnabled) { 238 | if (connection != null) { 239 | try { 240 | PreparedStatement statement = connection.prepareStatement("INSERT INTO ranking (uuid, name, score, wins, losses, discordTag) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE name = ?, score = ?, wins = ?, losses = ?, discordTag = ?"); 241 | statement.setString(1, member.player.uuid()); 242 | statement.setString(2, Strings.stripColors(member.player.name).substring(1)); 243 | statement.setInt(3, member.score); 244 | statement.setInt(4, member.wins); 245 | statement.setInt(5, member.losses); 246 | statement.setString(6, member.discordTag); 247 | statement.setString(7, Strings.stripColors(member.player.name).substring(1)); 248 | statement.setInt(8, member.score); 249 | statement.setInt(9, member.wins); 250 | statement.setInt(10, member.losses); 251 | statement.setString(11, member.discordTag); 252 | 253 | statement.executeQuery(); 254 | } catch (SQLException e) { 255 | e.printStackTrace(); 256 | } 257 | } 258 | } 259 | } 260 | 261 | public void calculateNewRankings(Faction winnerFaction, Faction looserFaction) { 262 | if (Config.c.databaseEnabled) { 263 | Member winner = winnerFaction.members.copy().sort(Comparator.comparingInt(a -> -a.score)).first(); 264 | Member looser = looserFaction.members.copy().sort(Comparator.comparingInt(a -> -a.score)).first(); 265 | 266 | double expectedValueWinner = 1 / (1 + Math.pow(10, ((double) Math.max(Math.min(looser.score - winner.score, 2000), -2000) / 2000))); 267 | double expectedValueLooser = 1 / (1 + Math.pow(10, ((double) Math.max(Math.min(winner.score - looser.score, 2000), -2000) / 2000))); 268 | 269 | int winnerScoreDiff = (int) ((looser.faction.maxCores * k + offset) * (1 - expectedValueWinner) * 1.5); 270 | int looserScoreDiff = (int) ((looser.faction.maxCores * k + offset) * (0 - expectedValueLooser)); 271 | 272 | int win = winnerScoreDiff / winnerFaction.members.size; 273 | int loss = looserScoreDiff / looserFaction.members.size; 274 | 275 | winnerFaction.members.each(m -> { 276 | MessageUtils.sendMessage(m.player, "You gained " + MessageUtils.cHighlight2 + win + MessageUtils.cDefault + " points", MessageUtils.MessageLevel.INFO); 277 | 278 | m.score += win; 279 | if (m.score < 0) m.score = 0; 280 | this.setRanking(m); 281 | }); 282 | 283 | looserFaction.members.each(m -> { 284 | MessageUtils.sendMessage(m.player, "You lost " + MessageUtils.cDanger + Math.abs(loss) + MessageUtils.cDefault + " points", MessageUtils.MessageLevel.INFO); 285 | 286 | m.losses++; 287 | m.score += loss; 288 | if (m.score < 0) m.score = 0; 289 | this.setRanking(m); 290 | }); 291 | } 292 | } 293 | 294 | public void calculateNewRankings(Faction looserFaction) { 295 | if (Config.c.databaseEnabled) { 296 | Member looser = looserFaction.members.first(); 297 | 298 | double expectedValueLooser = 1 / (1 + Math.pow(10, ((double) Math.max(Math.min(-looser.score, 2000), -2000) / 2000))); 299 | int looserScoreDiff = (int) ((looser.faction.maxCores * k + offset) * (0 - expectedValueLooser)); 300 | 301 | int loss = looserScoreDiff / looserFaction.members.size; 302 | 303 | looserFaction.members.each(m -> { 304 | MessageUtils.sendMessage(m.player, "You lost " + MessageUtils.cDanger + Math.abs(loss) + MessageUtils.cDefault + " points", MessageUtils.MessageLevel.INFO); 305 | 306 | m.losses++; 307 | m.score += loss; 308 | if (m.score < 0) m.score = 0; 309 | this.setRanking(m); 310 | }); 311 | } 312 | } 313 | 314 | private DBUrl readConfig() throws IOException { 315 | Gson gson = new Gson(); 316 | Reader reader = Files.newBufferedReader(Paths.get("config/mods/config/dbUrl.json")); 317 | DBUrl dbUrl = gson.fromJson(reader, DBUrl.class); 318 | reader.close(); 319 | return dbUrl; 320 | } 321 | 322 | private static class DBUrl { 323 | protected String url; 324 | protected String user; 325 | protected String password; 326 | 327 | public DBUrl(String url, String user, String password) { 328 | this.url = url; 329 | this.user = user; 330 | this.password = password; 331 | } 332 | } 333 | 334 | private static class LeaderBoardEntry { 335 | public final String name; 336 | public final int rank; 337 | public final int score; 338 | public final int wins; 339 | public final int losses; 340 | 341 | public LeaderBoardEntry(String name, int rank, int score, int wins, int losses) { 342 | this.name = name; 343 | this.rank = rank; 344 | this.score = score; 345 | this.wins = wins; 346 | this.losses = losses; 347 | } 348 | } 349 | } -------------------------------------------------------------------------------- /src/sectorized/sector/SectorManager.java: -------------------------------------------------------------------------------- 1 | package sectorized.sector; 2 | 3 | import arc.Events; 4 | import arc.graphics.Color; 5 | import arc.math.Mathf; 6 | import arc.math.geom.Point2; 7 | import arc.util.CommandHandler; 8 | import arc.util.Timer; 9 | import mindustry.content.Blocks; 10 | import mindustry.content.Fx; 11 | import mindustry.content.Planets; 12 | import mindustry.content.UnitTypes; 13 | import mindustry.entities.units.BuildPlan; 14 | import mindustry.game.EventType; 15 | import mindustry.game.Team; 16 | import mindustry.gen.Call; 17 | import mindustry.world.Tile; 18 | import mindustry.world.blocks.ConstructBlock; 19 | import mindustry.world.blocks.storage.CoreBlock; 20 | import sectorized.Manager; 21 | import sectorized.SectorizedEvents; 22 | import sectorized.constant.CoreCost; 23 | import sectorized.constant.MessageUtils; 24 | import sectorized.constant.State; 25 | import sectorized.constant.VaultLogic; 26 | import sectorized.faction.core.Member; 27 | import sectorized.sector.core.GridAccelerator; 28 | import sectorized.sector.core.SectorLogic; 29 | import sectorized.sector.core.SectorSpawns; 30 | 31 | import java.util.HashMap; 32 | 33 | import static mindustry.Vars.netServer; 34 | 35 | public class SectorManager implements Manager { 36 | private SectorLogic sectorLogic; 37 | private SectorSpawns sectorSpawns; 38 | 39 | private int corePlacementCooldown = 10; 40 | private HashMap bufferedCoresPlacement; 41 | 42 | @Override 43 | public void init() { 44 | Events.on(SectorizedEvents.BiomesGeneratedEvent.class, event -> { 45 | GridAccelerator gridAccelerator = new GridAccelerator(); 46 | sectorLogic = new SectorLogic(gridAccelerator); 47 | sectorSpawns = new SectorSpawns(gridAccelerator); 48 | 49 | bufferedCoresPlacement = new HashMap<>(); 50 | 51 | corePlacementCooldown = State.planet.equals(Planets.serpulo.name) ? 10 : 15; 52 | }); 53 | 54 | Events.on(SectorizedEvents.NewMemberEvent.class, event -> { 55 | try { 56 | Point2 spawnPoint = sectorSpawns.getNextFreeSpawn(); 57 | 58 | Events.fire(new SectorizedEvents.MemberSpawnedEvent(spawnPoint, event.member)); 59 | 60 | if (State.planet.equals(Planets.serpulo.name)) { 61 | sectorLogic.addArea(spawnPoint.x, spawnPoint.y, (CoreBlock) Blocks.coreFoundation, event.member.faction.team.id); 62 | } else if (State.planet.equals(Planets.erekir.name)) { 63 | sectorLogic.addArea(spawnPoint.x, spawnPoint.y, (CoreBlock) Blocks.coreAcropolis, event.member.faction.team.id); 64 | } 65 | } catch (SectorSpawns.NoSpawnPointAvailableException e) { 66 | Events.fire(new SectorizedEvents.NoSpawnPointAvailableEvent(event.member)); 67 | } 68 | }); 69 | 70 | Events.on(EventType.BlockBuildBeginEvent.class, event -> { 71 | if (State.gameState == State.GameState.INACTIVE) return; 72 | 73 | if (event.unit.type == UnitTypes.poly && !event.unit.plans.isEmpty()) { 74 | BuildPlan plan = event.unit.plans.first(); 75 | 76 | if (!sectorLogic.validPlace(plan.x, plan.y, plan.block, event.unit.team().id)) { 77 | event.unit.plans.removeFirst(); 78 | } 79 | } 80 | 81 | if (!sectorLogic.validPlace(event.tile.x, event.tile.y, event.tile.cblock() != null ? event.tile.cblock() : Blocks.conveyor, event.unit.team().id)) { 82 | event.tile.setNet(Blocks.air); 83 | } 84 | 85 | if (event.tile.build instanceof ConstructBlock.ConstructBuild constructBuild) { 86 | if (constructBuild.current == Blocks.coreFoundation || constructBuild.current == Blocks.coreNucleus) { 87 | sectorLogic.upgradeArea(event.tile.x, event.tile.y, (CoreBlock) constructBuild.current, event.team.id); 88 | } 89 | } 90 | }); 91 | 92 | Events.on(EventType.BlockDestroyEvent.class, event -> { 93 | if (State.gameState == State.GameState.INACTIVE) return; 94 | 95 | if (event.tile.block() == Blocks.shockMine) { 96 | Team team = event.tile.team(); 97 | 98 | Timer.schedule(() -> { 99 | if (sectorLogic.validBorder(event.tile.x, event.tile.y, team.id)) { 100 | Call.effectReliable(Fx.tapBlock, event.tile.getX(), event.tile.getY(), 0, Color.red); 101 | event.tile.setNet(Blocks.shockMine, team, 0); 102 | } 103 | }, Mathf.random(10.0f, 15.0f)); 104 | } 105 | }); 106 | 107 | Events.on(SectorizedEvents.CoreDestroyEvent.class, event -> { 108 | if (State.gameState == State.GameState.INACTIVE) return; 109 | 110 | sectorLogic.removeArea(event.coreBuild.tile.x, event.coreBuild.tile.y); 111 | 112 | for (Member member : event.faction.members) { 113 | Call.announce(member.player.con(), MessageUtils.cDanger + "You lost a core at " + MessageUtils.cHighlight1 + "[" + event.coreBuild.tile.x + " " + event.coreBuild.tile.y + "]"); 114 | } 115 | }); 116 | 117 | netServer.admins.addActionFilter(action -> { 118 | if (State.gameState == State.GameState.GAMEOVER) return false; 119 | if (State.gameState == State.GameState.INACTIVE) return false; 120 | 121 | switch (action.type) { 122 | case placeBlock: 123 | if (!sectorLogic.validPlace(action.tile.x, action.tile.y, action.block, action.player.team().id)) { 124 | MessageUtils.sendBufferedMessage(action.player, "You cannot build outside your sector! To expand your sector place a " + MessageUtils.cHighlight3 + "vault" + MessageUtils.cDefault + " \uF866 or " + MessageUtils.cHighlight3 + "reinforced vault" + MessageUtils.cDefault + " \uF70C within the borders of your sector!", MessageUtils.MessageLevel.WARNING); 125 | return false; 126 | } 127 | 128 | if ((action.block == Blocks.vault || action.block == Blocks.reinforcedVault) && action.tile.cblock() == Blocks.air && !VaultLogic.adjacentToCore(action.tile.x, action.tile.y, action.block)) { 129 | Tile tile = action.tile; 130 | 131 | if (action.block == Blocks.reinforcedVault) { 132 | boolean placeFound = false; 133 | 134 | for (int i = 0; i < 4; i++) { 135 | BuildPlan plan = new BuildPlan(tile.x - (i % 2), tile.y - (i / 2), 0, Blocks.titan); 136 | 137 | if (plan.placeable(action.player.team())) { 138 | placeFound = true; 139 | tile = plan.tile(); 140 | break; 141 | } 142 | } 143 | 144 | if (!placeFound) { 145 | MessageUtils.sendBufferedMessage(action.player, "Cannot place a Core Bastion here! (4x4 area required)", MessageUtils.MessageLevel.WARNING, 1); 146 | return false; 147 | } 148 | } 149 | 150 | if (bufferedCoresPlacement.containsKey(action.player.team().id)) { 151 | int seconds = (action.player.team().cores().size <= 2 ? 30 : corePlacementCooldown) - (int) Math.floor((State.time - bufferedCoresPlacement.get(action.player.team().id)) / 60); 152 | 153 | MessageUtils.sendBufferedMessage(action.player, "Wait " + MessageUtils.cInfo + seconds + " seconds" + MessageUtils.cDefault + " to place a new core!", MessageUtils.MessageLevel.WARNING, 1); 154 | tile.setNet(Blocks.air); 155 | } else if (CoreCost.checkAndConsumeFunds(action.player.team())) { 156 | if (State.planet.equals(Planets.serpulo.name)) { 157 | tile.setNet(Blocks.coreShard, action.player.team(), 0); 158 | sectorLogic.addArea(tile.x, tile.y, (CoreBlock) Blocks.coreShard, action.player.team().id); 159 | } else if (State.planet.equals(Planets.erekir.name)) { 160 | tile.setNet(Blocks.coreBastion, action.player.team(), 0); 161 | sectorLogic.addArea(tile.x, tile.y, (CoreBlock) Blocks.coreBastion, action.player.team().id); 162 | } 163 | 164 | int team = action.player.team().id; 165 | bufferedCoresPlacement.put(team, State.time); 166 | Timer.schedule(() -> bufferedCoresPlacement.remove(team), action.player.team().cores().size <= 2 ? 30 : corePlacementCooldown); 167 | 168 | Events.fire(new SectorizedEvents.CoreBuildEvent(tile)); 169 | } else { 170 | MessageUtils.sendBufferedMessage(action.player, "Insufficient resources for a new core", MessageUtils.MessageLevel.WARNING, 1); 171 | tile.setNet(Blocks.air); 172 | } 173 | 174 | return false; 175 | } 176 | break; 177 | case breakBlock: 178 | if (action.block == Blocks.shockMine) { 179 | return false; 180 | } 181 | break; 182 | } 183 | 184 | return true; 185 | }); 186 | } 187 | 188 | @Override 189 | public void reset() { 190 | 191 | } 192 | 193 | @Override 194 | public void registerServerCommands(CommandHandler handler) { 195 | 196 | } 197 | 198 | @Override 199 | public void registerClientCommands(CommandHandler handler) { 200 | 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/sectorized/sector/core/GridAccelerator.java: -------------------------------------------------------------------------------- 1 | package sectorized.sector.core; 2 | 3 | import sectorized.sector.core.objects.Rectangle; 4 | import sectorized.sector.core.objects.SubRectangle; 5 | 6 | import java.util.*; 7 | 8 | import static mindustry.Vars.world; 9 | 10 | public class GridAccelerator { 11 | private final static int cellSize = 100; 12 | 13 | private final ArrayList[][] grid; 14 | 15 | public GridAccelerator() { 16 | grid = new ArrayList[world.width()][world.height()]; 17 | 18 | Arrays.stream(grid).forEach(array -> Arrays.fill(array, new ArrayList())); 19 | } 20 | 21 | private void iterate(Rectangle rectangle, Iterator iterator) { 22 | for (int x = Math.max(rectangle.minX / cellSize, 0); x <= Math.min(rectangle.maxX / cellSize, world.width() - 1); x++) { 23 | for (int y = Math.max(rectangle.minY / cellSize, 0); y <= Math.min(rectangle.maxY / cellSize, world.height() - 1); y++) { 24 | iterator.apply(x, y); 25 | } 26 | } 27 | } 28 | 29 | protected void addRectangle(Rectangle rectangle) { 30 | iterate(rectangle, (x, y) -> grid[x][y].add(rectangle)); 31 | } 32 | 33 | protected void removeRectangle(Rectangle rectangle) { 34 | iterate(rectangle, (x, y) -> grid[x][y].remove(rectangle)); 35 | } 36 | 37 | protected SubRectangle[] getIntersectingRectangles(Rectangle rectangle) { 38 | HashSet intersectingRectangles = new HashSet<>(); 39 | 40 | LinkedHashSet rectangles = new LinkedHashSet<>(); 41 | iterate(rectangle, (x, y) -> rectangles.addAll(grid[x][y])); 42 | 43 | for (Rectangle rect : rectangles) { 44 | SubRectangle intersectingRectangle = rect.intersect(rectangle); 45 | if (intersectingRectangle != null) intersectingRectangles.add(intersectingRectangle); 46 | } 47 | 48 | SubRectangle[] subRectangles = intersectingRectangles.toArray(new SubRectangle[0]); 49 | Arrays.sort(subRectangles, Comparator.comparingInt(a -> -a.zIndex)); 50 | 51 | return subRectangles; 52 | } 53 | 54 | @FunctionalInterface 55 | private interface Iterator { 56 | void apply(int x, int y); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/sectorized/sector/core/SectorLogic.java: -------------------------------------------------------------------------------- 1 | package sectorized.sector.core; 2 | 3 | import arc.graphics.Color; 4 | import arc.math.Mathf; 5 | import arc.math.geom.Point2; 6 | import arc.util.Time; 7 | import mindustry.content.Blocks; 8 | import mindustry.content.Bullets; 9 | import mindustry.content.Fx; 10 | import mindustry.game.Team; 11 | import mindustry.gen.Building; 12 | import mindustry.gen.Call; 13 | import mindustry.type.Item; 14 | import mindustry.world.Block; 15 | import mindustry.world.Tile; 16 | import mindustry.world.blocks.storage.CoreBlock; 17 | import sectorized.constant.Constants; 18 | import sectorized.sector.core.objects.Border; 19 | import sectorized.sector.core.objects.Rectangle; 20 | import sectorized.sector.core.objects.SubRectangle; 21 | 22 | import java.util.Arrays; 23 | import java.util.HashSet; 24 | import java.util.LinkedHashMap; 25 | 26 | import static mindustry.Vars.*; 27 | 28 | public class SectorLogic { 29 | private final int[][] sectors; 30 | private final GridAccelerator gridAccelerator; 31 | private final LinkedHashMap coreBuildRectangleMap = new LinkedHashMap<>(); 32 | private int nextZIndex = 0; 33 | 34 | public SectorLogic(GridAccelerator gridAccelerator) { 35 | sectors = new int[world.width()][world.height()]; 36 | this.gridAccelerator = gridAccelerator; 37 | 38 | Arrays.stream(sectors).forEach(array -> Arrays.fill(array, 0)); 39 | } 40 | 41 | public boolean validPlace(int x, int y, Block block, int teamId) { 42 | int offset = -(block.size - 1) / 2; 43 | 44 | for (int dx = 0; dx < block.size; dx++) { 45 | for (int dy = 0; dy < block.size; dy++) { 46 | int wx = dx + offset + x, wy = dy + offset + y; 47 | 48 | if (!validPlace(wx, wy, teamId)) return false; 49 | } 50 | } 51 | 52 | return true; 53 | } 54 | 55 | public boolean validBorder(int x, int y, int teamId) { 56 | if (sectors[x][y] != -1) return false; 57 | 58 | for (int dx = -1; dx <= 1; dx++) { 59 | for (int dy = -1; dy <= 1; dy++) { 60 | if (sectors[Math.max(0, Math.min(x + dx, world.width() - 1))][Math.max(0, Math.min(y + dy, world.height() - 1))] == teamId) { 61 | return true; 62 | } 63 | } 64 | } 65 | return false; 66 | } 67 | 68 | public void addArea(int coreX, int coreY, CoreBlock coreBlock, int teamId) { 69 | int size = Constants.radii.get(coreBlock); 70 | 71 | Rectangle addRectangle = new Rectangle(Math.max(coreX - size, 0), 72 | Math.max(coreY - size, 0), 73 | Math.min(coreX + size, world.width() - 1), 74 | Math.min(coreY + size, world.height() - 1), 75 | teamId, 76 | nextZIndex++); 77 | 78 | coreBuildRectangleMap.put(Point2.pack(coreX, coreY), addRectangle); 79 | 80 | SubRectangle[] subRectangles = gridAccelerator.getIntersectingRectangles(addRectangle); 81 | gridAccelerator.addRectangle(addRectangle); 82 | 83 | HashSet borderHashSet = new HashSet<>(size * 6); 84 | 85 | addRectangle.iterate((x, y, borderX, borderY) -> { 86 | Tile tile = world.tile(x, y); 87 | if (tile.block() == Blocks.shockMine) { 88 | destroyTileWithoutPolyRebuild(tile); 89 | } 90 | 91 | if (borderX || borderY) { 92 | borderHashSet.add(new Border(x, y, borderX, borderY, addRectangle.teamId)); 93 | } else { 94 | sectors[x][y] = addRectangle.teamId; 95 | } 96 | }); 97 | 98 | handleSubRectanglesAndBorder(subRectangles, borderHashSet); 99 | } 100 | 101 | public void upgradeArea(int coreX, int coreY, CoreBlock coreBlock, int teamId) { 102 | for (int x = -1; x <= 1; x++) { 103 | for (int y = -1; y <= 1; y++) { 104 | Tile nearby = world.tile(coreX, coreY).nearby(x, y); 105 | 106 | if (coreBuildRectangleMap.containsKey(nearby.pos())) { 107 | Rectangle removeRectangle = coreBuildRectangleMap.remove(nearby.pos()); 108 | gridAccelerator.removeRectangle(removeRectangle); 109 | addArea(coreX, coreY, coreBlock, teamId); 110 | 111 | return; 112 | } 113 | } 114 | } 115 | } 116 | 117 | public void removeArea(int coreX, int coreY) { 118 | Tile coreTile = world.tile(coreX, coreY); 119 | int size = Constants.radii.get((CoreBlock) coreTile.block()); 120 | Rectangle removeRectangle = coreBuildRectangleMap.remove(coreTile.pos()); 121 | 122 | if (removeRectangle == null) return; 123 | 124 | gridAccelerator.removeRectangle(removeRectangle); 125 | SubRectangle[] subRectangles = gridAccelerator.getIntersectingRectangles(removeRectangle); 126 | 127 | removeRectangle.iterate((x, y, borderX, borderY) -> { 128 | Tile tile = world.tile(x, y); 129 | if (tile.block() == Blocks.shockMine) { 130 | destroyTileWithoutPolyRebuild(tile); 131 | } 132 | sectors[x][y] = 0; 133 | }); 134 | 135 | HashSet borderHashSet = new HashSet<>(size * 6); 136 | handleSubRectanglesAndBorder(subRectangles, borderHashSet); 137 | 138 | removeRectangle.iterate((x, y, borderX, borderY) -> { 139 | int sector = sectors[x][y]; 140 | 141 | if (!(borderX || borderY) && sector >= 0 && sector != world.tile(x, y).team().id) { 142 | destroyTileWithoutPolyRebuild(world.tile(x, y)); 143 | } 144 | }); 145 | } 146 | 147 | private boolean validPlace(int x, int y, int teamId) { 148 | return sectors[x][y] == teamId; 149 | } 150 | 151 | private void handleSubRectanglesAndBorder(SubRectangle[] subRectangles, HashSet borderHashSet) { 152 | for (SubRectangle subRectangle : subRectangles) { 153 | subRectangle.iterate((x, y, borderX, borderY) -> { 154 | Border border = new Border(x, y, borderX, borderY, subRectangle.teamId, sectors[x][y]); 155 | 156 | borderHashSet.remove(border); 157 | 158 | if ((borderX || borderY) && sectors[x][y] != subRectangle.teamId) borderHashSet.add(border); 159 | else sectors[x][y] = subRectangle.teamId; 160 | }); 161 | } 162 | 163 | for (Border border : borderHashSet) { 164 | sectors[border.x][border.y] = -1; 165 | Tile tile = world.tile(border.x, border.y); 166 | if (tile.build != null) destroyTileWithoutPolyRebuild(tile); 167 | 168 | if (tile.passable()) { 169 | if (border.borderX) { 170 | if (border.y % 2 == 0 && !border.borderY) { 171 | if (border.team2Id > 0) 172 | tile.setNet(Blocks.shockMine, Team.get(border.team2Id), 0); 173 | } else if (border.team1Id > 0) { 174 | tile.setNet(Blocks.shockMine, Team.get(border.team1Id), 0); 175 | } 176 | } 177 | if (border.borderY) { 178 | if (border.x % 2 == 0 && !border.borderX) { 179 | if (border.team2Id > 0) 180 | tile.setNet(Blocks.shockMine, Team.get(border.team2Id), 0); 181 | } else if (border.team1Id > 0) { 182 | tile.setNet(Blocks.shockMine, Team.get(border.team1Id), 0); 183 | } 184 | } 185 | } 186 | } 187 | } 188 | 189 | private void destroyTileWithoutPolyRebuild(Tile tile) { 190 | Building building = tile.build; 191 | 192 | if (building != null) { 193 | float explosiveness = building.block.baseExplosiveness; 194 | float flammability = 0.0F; 195 | if (building.block.hasItems) { 196 | for (Item item : content.items()) { 197 | int amount = building.items.get(item); 198 | explosiveness += item.explosiveness * amount; 199 | flammability += item.flammability * amount; 200 | } 201 | } 202 | if (building.block.hasLiquids) { 203 | flammability += building.liquids.sum((liquid, amount) -> liquid.flammability * amount / 2.0F); 204 | explosiveness += building.liquids.sum((liquid, amount) -> liquid.explosiveness * amount / 2.0F); 205 | } 206 | 207 | float radius = tilesize * building.block.size / 2.0F; 208 | if (state.rules.damageExplosions) { 209 | for (int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i++) { 210 | Time.run(i / 2f, () -> Call.createBullet(Bullets.fireball, Team.derelict, building.x, building.y, Mathf.random(360f), Bullets.fireball.damage, 1, 1)); 211 | } 212 | 213 | int waves = Mathf.clamp((int) (explosiveness / 4), 0, 30); 214 | 215 | for (int i = 0; i < waves; i++) { 216 | Time.run(i * 2f, () -> Call.effect(Fx.blockExplosionSmoke, building.x + Mathf.range(radius), building.y + Mathf.range(radius), 0, Color.white)); 217 | } 218 | } 219 | 220 | if (explosiveness > 15f) { 221 | Call.effect(Fx.shockwave, building.x, building.y, 0, Color.white); 222 | } 223 | 224 | if (explosiveness > 30f) { 225 | Call.effect(Fx.bigShockwave, building.x, building.y, 0, Color.white); 226 | } 227 | 228 | Call.effectReliable(Fx.dynamicExplosion, building.x, building.y, radius / 8f, Color.white); 229 | 230 | tile.setNet(Blocks.air); 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/sectorized/sector/core/SectorSpawns.java: -------------------------------------------------------------------------------- 1 | package sectorized.sector.core; 2 | 3 | import arc.math.Mathf; 4 | import arc.math.geom.Point2; 5 | import arc.struct.Seq; 6 | import arc.util.noise.Simplex; 7 | import mindustry.content.Blocks; 8 | import mindustry.content.Planets; 9 | import mindustry.entities.units.BuildPlan; 10 | import mindustry.game.Team; 11 | import mindustry.world.Block; 12 | import mindustry.world.Tile; 13 | import mindustry.world.blocks.environment.Floor; 14 | import mindustry.world.blocks.storage.CoreBlock; 15 | import sectorized.constant.Constants; 16 | import sectorized.constant.State; 17 | import sectorized.sector.core.objects.Rectangle; 18 | import sectorized.sector.core.objects.SubRectangle; 19 | 20 | import static mindustry.Vars.*; 21 | 22 | public class SectorSpawns { 23 | private final Seq spawnSeq = new Seq<>(); 24 | private final GridAccelerator gridAccelerator; 25 | private int spawningSearchStartIndex = 0; 26 | 27 | public SectorSpawns(GridAccelerator gridAccelerator) { 28 | this.gridAccelerator = gridAccelerator; 29 | 30 | final int size = Constants.radii.get((CoreBlock) Blocks.coreNucleus); 31 | final int rObstacle = 5; 32 | 33 | for (int x = 0; x + Constants.spawnCellSize <= world.width(); x += Constants.spawnCellSize) { 34 | for (int y = 0; y + Constants.spawnCellSize <= world.height(); y += Constants.spawnCellSize) { 35 | int rx = Mathf.random(Constants.spawnCellSize / 2) - Constants.spawnCellSize / 4 + x + Constants.spawnCellSize / 2; 36 | int ry = Mathf.random(Constants.spawnCellSize / 2) - Constants.spawnCellSize / 4 + y + Constants.spawnCellSize / 2; 37 | 38 | BuildPlan buildPlan = new BuildPlan(rx, ry, 0, Blocks.multiplicativeReconstructor); 39 | if (!buildPlan.placeable(Team.sharded)) 40 | continue; 41 | 42 | int hasSurfaceCount = 0; 43 | for (int i = Math.max(rx - size, 0); i < Math.min(rx + size, world.width() - 1); i += 10) { 44 | for (int j = Math.max(ry - size, 0); j < Math.min(ry + size, world.height() - 1); j += 10) { 45 | if (world.tile(i, j).floor().hasSurface()) hasSurfaceCount++; 46 | } 47 | } 48 | 49 | boolean obstacle = false; 50 | for (int i = Math.max(rx - rObstacle, 0); i < Math.min(rx + rObstacle, world.width() - 1); i++) { 51 | for (int j = Math.max(ry - rObstacle, 0); j < Math.min(ry + rObstacle, world.height() - 1); j++) { 52 | Block block = world.tile(i, j).block(); 53 | if (!world.tile(i, j).floor().hasSurface() || (block != null && block.solid)) obstacle = true; 54 | } 55 | } 56 | 57 | if (!obstacle && hasSurfaceCount > size * size / 300) { 58 | spawnSeq.add(new Point2(rx, ry)); 59 | } 60 | } 61 | } 62 | 63 | int radius = (int) (state.rules.dropZoneRadius / tilesize); 64 | final int seed = Mathf.random(100_000); 65 | 66 | int offset = Mathf.random(5) + 1; 67 | for (int i = 0; i < 4; i++) { 68 | Point2 center = new Point2(i % 4 >= 2 ? world.width() : 0, i % 2 == 1 ? world.height() : 0); 69 | 70 | spawnSeq.sort((Point2 a, Point2 b) -> (Math.abs(center.x - b.x) + Math.abs(center.y - b.y)) - (Math.abs(center.x - a.x) + Math.abs(center.y - a.y))); 71 | 72 | Tile tile = world.tile(spawnSeq.get(Math.min(offset, spawnSeq.size - 1)).pack()); 73 | spawnSeq.remove(offset); 74 | 75 | for (int x = Math.max(0, tile.x - radius); x <= Math.min(world.width() - 1, tile.x + radius); x++) { 76 | for (int y = Math.max(0, tile.y - radius); y <= Math.min(world.height() - 1, tile.y + radius); y++) { 77 | if (State.planet.equals(Planets.serpulo.name)) { 78 | double t = Simplex.noise2d(seed, 10, 0.5, 0.1, x, y); 79 | 80 | if (world.tile(x, y).floor().hasSurface() ? t < 0.6 : t < 0.3) { 81 | int dist = (int) tile.dst(x * tilesize, y * tilesize) / tilesize; 82 | 83 | if (dist < radius) { 84 | int b = Mathf.random(4); 85 | 86 | switch (b) { 87 | case 0: 88 | world.tile(x, y).setFloor((Floor) Blocks.darkPanel1); 89 | break; 90 | case 1: 91 | world.tile(x, y).setFloor((Floor) Blocks.darkPanel2); 92 | break; 93 | case 2: 94 | world.tile(x, y).setFloor((Floor) Blocks.darkPanel3); 95 | break; 96 | case 3: 97 | world.tile(x, y).setFloor((Floor) Blocks.darkPanel4); 98 | break; 99 | case 4: 100 | world.tile(x, y).setFloor((Floor) Blocks.darkPanel5); 101 | break; 102 | } 103 | } else if (dist == radius) { 104 | world.tile(x, y).setFloor((Floor) Blocks.metalFloor5); 105 | } 106 | } 107 | } else if (State.planet.equals(Planets.erekir.name)) { 108 | int dist = (int) tile.dst(x * tilesize, y * tilesize) / tilesize; 109 | 110 | if (dist <= radius && dist >= radius - 1) { 111 | world.tile(x, y).setFloor((Floor) Blocks.coreZone); 112 | } 113 | } 114 | } 115 | } 116 | 117 | tile.setOverlay(Blocks.spawn); 118 | } 119 | 120 | spawnSeq.sort((Point2 a, Point2 b) -> Math.max(Math.abs(world.width() / 2 - b.x), Math.abs(world.width() / 2 - b.y)) - Math.max(Math.abs(world.width() / 2 - a.x), Math.abs(world.width() / 2 - a.y))); 121 | 122 | for (int i = 0; i < spawnSeq.size - 1; i++) { 123 | Point2 a = spawnSeq.get(i); 124 | int r = Math.min(Mathf.random(spawnSeq.size / 10) + i, spawnSeq.size - 1); 125 | Point2 b = spawnSeq.get(r); 126 | 127 | spawnSeq.set(i, b); 128 | spawnSeq.set(r, a); 129 | } 130 | } 131 | 132 | public Point2 getNextFreeSpawn() throws NoSpawnPointAvailableException { 133 | int size = Constants.radii.get((CoreBlock) Blocks.coreNucleus); 134 | 135 | for (int i = 0; i < spawnSeq.size; i++) { 136 | Point2 p = spawnSeq.get((i + spawningSearchStartIndex) % spawnSeq.size); 137 | 138 | SubRectangle[] subRectangles = gridAccelerator.getIntersectingRectangles(new Rectangle(Math.max(p.x - size, 0), 139 | Math.max(p.y - size, 0), 140 | Math.min(p.x + size, world.width() - 1), 141 | Math.min(p.y + size, world.height() - 1), 142 | -1, 143 | -1)); 144 | 145 | if (subRectangles.length == 0) { 146 | spawningSearchStartIndex = (i + spawningSearchStartIndex + 1) % this.spawnSeq.size; 147 | return p; 148 | } 149 | } 150 | 151 | throw new NoSpawnPointAvailableException(); 152 | } 153 | 154 | public static class NoSpawnPointAvailableException extends Exception { 155 | public NoSpawnPointAvailableException() { 156 | super("No spawn point is available at the moment"); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/sectorized/sector/core/objects/Border.java: -------------------------------------------------------------------------------- 1 | package sectorized.sector.core.objects; 2 | 3 | import java.util.Objects; 4 | 5 | public class Border { 6 | public final int x, y, team1Id, team2Id; 7 | public final boolean borderX, borderY; 8 | 9 | public Border(int x, int y, boolean borderX, boolean borderY, int team1Id, int team2Id) { 10 | this.x = x; 11 | this.y = y; 12 | this.borderX = borderX; 13 | this.borderY = borderY; 14 | this.team1Id = team1Id; 15 | this.team2Id = team2Id; 16 | } 17 | 18 | public Border(int x, int y, boolean borderX, boolean borderY, int team1Id) { 19 | this(x, y, borderX, borderY, team1Id, 0); 20 | } 21 | 22 | @Override 23 | public boolean equals(Object o) { 24 | if (this == o) return true; 25 | if (o == null || getClass() != o.getClass()) return false; 26 | Border border = (Border) o; 27 | return x == border.x && y == border.y; 28 | } 29 | 30 | @Override 31 | public int hashCode() { 32 | return Objects.hash(x, y); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/sectorized/sector/core/objects/Rectangle.java: -------------------------------------------------------------------------------- 1 | package sectorized.sector.core.objects; 2 | 3 | public class Rectangle { 4 | public final int minX, minY, maxX, maxY; 5 | public final int teamId, zIndex; 6 | 7 | public Rectangle(int minX, int minY, int maxX, int maxY, int teamId, int zIndex) { 8 | this.minX = minX; 9 | this.minY = minY; 10 | this.maxX = maxX; 11 | this.maxY = maxY; 12 | this.teamId = teamId; 13 | this.zIndex = zIndex; 14 | } 15 | 16 | public void iterate(Iterator iterator) { 17 | for (int x = minX; x <= maxX; x++) { 18 | for (int y = minY; y <= maxY; y++) { 19 | iterator.apply(x, y, x == minX || x == maxX, y == minY || y == maxY); 20 | } 21 | } 22 | } 23 | 24 | public SubRectangle intersect(Rectangle rectangle) { 25 | int minMaxX = Math.min(maxX, rectangle.maxX); 26 | int maxMinX = Math.max(minX, rectangle.minX); 27 | int minMaxY = Math.min(maxY, rectangle.maxY); 28 | int maxMinY = Math.max(minY, rectangle.minY); 29 | 30 | if (maxMinY <= minMaxY && maxMinX <= minMaxX) { 31 | return new SubRectangle(maxMinX, maxMinY, minMaxX, minMaxY, this); 32 | } 33 | 34 | return null; 35 | } 36 | 37 | @FunctionalInterface 38 | public interface Iterator { 39 | void apply(int x, int y, boolean borderX, boolean borderY); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/sectorized/sector/core/objects/SubRectangle.java: -------------------------------------------------------------------------------- 1 | package sectorized.sector.core.objects; 2 | 3 | public class SubRectangle extends Rectangle { 4 | public final Rectangle parent; 5 | 6 | public SubRectangle(int minX, int minY, int maxX, int maxY, Rectangle parent) { 7 | super(minX, minY, maxX, maxY, parent.teamId, parent.zIndex); 8 | this.parent = parent; 9 | } 10 | 11 | public void iterate(Iterator iterator) { 12 | for (int x = minX; x <= maxX; x++) { 13 | for (int y = minY; y <= maxY; y++) { 14 | iterator.apply(x, y, x == parent.minX || x == parent.maxX, y == parent.minY || y == parent.maxY); 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/sectorized/update/UpdateManager.java: -------------------------------------------------------------------------------- 1 | package sectorized.update; 2 | 3 | import arc.Core; 4 | import arc.Events; 5 | import arc.struct.Seq; 6 | import arc.util.Timer; 7 | import arc.util.*; 8 | import mindustry.Vars; 9 | import mindustry.content.Planets; 10 | import mindustry.content.UnitTypes; 11 | import mindustry.game.EventType; 12 | import mindustry.game.Team; 13 | import mindustry.gen.Call; 14 | import mindustry.gen.Groups; 15 | import mindustry.gen.Player; 16 | import mindustry.net.Administration; 17 | import mindustry.net.Packets; 18 | import mindustry.type.ItemSeq; 19 | import mindustry.type.ItemStack; 20 | import sectorized.Manager; 21 | import sectorized.SectorizedEvents; 22 | import sectorized.constant.*; 23 | import sectorized.world.map.Biomes; 24 | 25 | import java.util.*; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | import static mindustry.Vars.netServer; 29 | import static mindustry.Vars.state; 30 | 31 | public class UpdateManager implements Manager { 32 | private final Interval interval = new Interval(3); 33 | 34 | private final HashSet hideHud = new HashSet<>(); 35 | 36 | private int infoMessageIndex = 0; 37 | 38 | private final HashMap biomeVotes = new HashMap<>(); 39 | private boolean biomeVoteFinished = false; 40 | 41 | private int coreDominationDifference = 3; 42 | 43 | @Override 44 | public void init() { 45 | Events.run(EventType.Trigger.update, () -> { 46 | if (State.gameState == State.GameState.INACTIVE || State.gameState == State.GameState.GAMEOVER) return; 47 | 48 | State.time += Time.delta; 49 | 50 | if (interval.get(0, 60 * 5)) { 51 | double blockDamageMultiplier = Math.round(state.rules.blockDamageMultiplier * 10.0) / 10.0; 52 | double unitDamageMultiplier = Math.round(state.rules.unitDamageMultiplier * 10.0) / 10.0; 53 | double unitHealthMultiplier = Math.round(Units.healthMultiplier * 10.0) / 10.0; 54 | 55 | Groups.player.each(player -> (!hideHud.contains(player.uuid()) && player.team() != Team.derelict), player -> { 56 | int cores = player.team().cores().size - 1; 57 | 58 | StringBuilder infoPopupText = State.planet.equals(Planets.serpulo.name) ? new StringBuilder(MessageUtils.cInfo + "Costs for next[white] \uF869\n") : new StringBuilder(MessageUtils.cInfo + "Costs for next[white] \uF725\n"); 59 | 60 | ItemSeq requirements = CoreCost.getRequirements(player.team()); 61 | 62 | for (ItemStack itemStack : requirements) { 63 | int availableItems = player.team().items().get(itemStack.item); 64 | 65 | infoPopupText 66 | .append(CoreCost.itemUnicodes.get(itemStack.item)) 67 | .append(availableItems >= itemStack.amount ? itemStack.amount + MessageUtils.cHighlight2 + "\uE800[white]" : MessageUtils.cDanger + availableItems + "[white]/" + itemStack.amount) 68 | .append("\n"); 69 | } 70 | 71 | infoPopupText.append(MessageUtils.cHighlight3) 72 | .append("\nMultipliers:[white]\n") 73 | .append("\uF856 ") 74 | .append(blockDamageMultiplier) 75 | .append(" \uF7F4 ") 76 | .append(unitDamageMultiplier) 77 | .append(" \uf848 ") 78 | .append(unitHealthMultiplier) 79 | .append("\n"); 80 | 81 | infoPopupText.append(MessageUtils.cWarning) 82 | .append("\nUnit Cap: [white]") 83 | .append(mindustry.entities.Units.getCap(player.team())) 84 | .append("\n"); 85 | 86 | if (State.gameState == State.GameState.LOCKED) { 87 | infoPopupText.append("\n(Re)spawning " + MessageUtils.cInfo + "locked[white]\n"); 88 | } 89 | 90 | infoPopupText.append("\nToggle with " + MessageUtils.cHighlight3 + "/hud [white]"); 91 | 92 | Call.infoPopupReliable(player.con, infoPopupText.toString(), 5.01f, Align.topLeft, 90, 5, 0, 0); 93 | }); 94 | } 95 | 96 | if (interval.get(1, 60 * 60 * 8)) { 97 | switch (infoMessageIndex) { 98 | case 0: 99 | MessageUtils.sendMessage("In addition to commanding your units you can also use " + MessageUtils.cHighlight3 + "/attack /defend /idle" + MessageUtils.cDefault + ".\n" + 100 | "You can request to join another team using " + MessageUtils.cHighlight3 + "/join" + MessageUtils.cDefault + "\n" + 101 | "Join the discord and be part of the community! \n" + 102 | MessageUtils.cPlayer + "\uE80D" + MessageUtils.cDefault + " https://discord.gg/AmdMXKkS9Q", MessageUtils.MessageLevel.INFO); 103 | break; 104 | case 1: 105 | MessageUtils.sendMessage("Type " + MessageUtils.cHighlight3 + "/score" + MessageUtils.cDefault + " or " + MessageUtils.cHighlight3 + "/leaderboard" + MessageUtils.cDefault + " to display your rank!\n" + 106 | MessageUtils.cPlayer + "\uE80D" + MessageUtils.cDefault + " https://discord.gg/AmdMXKkS9Q", MessageUtils.MessageLevel.INFO); 107 | break; 108 | case 2: 109 | MessageUtils.sendMessage("If you are dominating the game end it as soon as possible, others want to play as well!\n" + 110 | MessageUtils.cPlayer + "\uE80D" + MessageUtils.cDefault + " https://discord.gg/AmdMXKkS9Q", MessageUtils.MessageLevel.INFO); 111 | break; 112 | } 113 | 114 | infoMessageIndex = (infoMessageIndex + 1) % 3; 115 | } 116 | 117 | if (interval.get(2, 60 * 60 * 2)) { 118 | if (Groups.player.size() < 2 || state.wave < 15) return; 119 | 120 | Team dominatingTeam = null; 121 | boolean lock = false; 122 | 123 | Team[] teams = Team.all.clone(); 124 | Arrays.sort(teams, Comparator.comparingInt(t -> -t.cores().size)); 125 | if (teams[0].cores().size >= teams[1].cores().size + coreDominationDifference + (state.wave * 0.1)) { 126 | dominatingTeam = teams[0]; 127 | lock = true; 128 | } 129 | 130 | if (!lock) { 131 | HashMap healthPerTeam = new HashMap<>(); 132 | 133 | Groups.unit.each(u -> u.team != Team.crux && 134 | u.type != UnitTypes.mono && 135 | u.type != UnitTypes.poly 136 | , u -> { 137 | if (u.team != Team.crux) 138 | healthPerTeam.put(u.team, healthPerTeam.getOrDefault(u.team, 0.0f) + u.health()); 139 | }); 140 | 141 | List> healthPerTeamEntries = new ArrayList<>(healthPerTeam.entrySet()); 142 | healthPerTeamEntries.sort((Map.Entry a, Map.Entry b) -> (int) (b.getValue() - a.getValue())); 143 | 144 | if (healthPerTeamEntries.size() > 1 && healthPerTeamEntries.get(0).getValue() >= healthPerTeamEntries.get(1).getValue() + 10000 + 1000 * state.wave) { 145 | dominatingTeam = healthPerTeamEntries.get(0).getKey(); 146 | lock = true; 147 | } 148 | } 149 | 150 | if (State.gameState == State.GameState.ACTIVE && lock) { 151 | State.gameState = State.GameState.LOCKED; 152 | state.rules.waveSpacing = state.rules.waveSpacing / 2; 153 | Events.fire(new SectorizedEvents.TeamDominatingEvent(dominatingTeam)); 154 | } else if (State.gameState == State.GameState.LOCKED && !lock) { 155 | State.gameState = State.GameState.ACTIVE; 156 | state.rules.waveSpacing = state.rules.waveSpacing * 2; 157 | Events.fire(new SectorizedEvents.NoTeamDominatingEvent()); 158 | } 159 | } 160 | }); 161 | 162 | Events.on(SectorizedEvents.BiomesGeneratedEvent.class, event -> { 163 | coreDominationDifference = State.planet.equals(Planets.serpulo.name) ? 3 : 5; 164 | }); 165 | 166 | Events.on(SectorizedEvents.GamemodeStartEvent.class, event -> { 167 | setServerDescription(); 168 | }); 169 | 170 | Events.on(EventType.WaveEvent.class, event -> { 171 | if (State.gameState == State.GameState.INACTIVE || State.gameState == State.GameState.GAMEOVER) return; 172 | 173 | state.rules.loadout = Loadout.getLoadout(state.wave); 174 | 175 | state.rules.blockDamageMultiplier = (float) (1 + (1 / (Math.pow(state.wave * 0.05, 4) + 1))); 176 | state.rules.unitDamageMultiplier = (float) (3 - (2 / (Math.pow(state.wave * 0.02, 4) + 1))); 177 | 178 | Units.setUnitHealthMultiplier((float) (6 - (5 / (Math.pow(state.wave * 0.02, 4) + 1)))); 179 | 180 | if (state.teams.active.size < 2 && state.wave >= 5) { 181 | DiscordBot.sendMessage("**Game Over!** Crux won the game in " + Vars.state.wave + " waves."); 182 | 183 | Events.fire(new SectorizedEvents.RestartEvent("No teams left")); 184 | } 185 | 186 | setServerDescription(); 187 | }); 188 | 189 | Events.on(EventType.PlayerJoin.class, event -> { 190 | setServerDescription(); 191 | }); 192 | 193 | Events.on(EventType.PlayerLeave.class, event -> { 194 | setServerDescription(); 195 | }); 196 | 197 | MenuUtils.addMenu(20, player -> { 198 | return new MenuUtils.MenuContent( 199 | "GAME OVER - Planet Vote", 200 | (State.winner == null ? MessageUtils.cDanger + "Crux[white] won the game in " + MessageUtils.cInfo + Vars.state.wave + "[white] waves!\n\n" : MessageUtils.cPlayer + State.winner.player.name + "[white] won the game in " + MessageUtils.cInfo + Vars.state.wave + "[white] waves!\n\n") + 201 | "Vote for a planet or biome you want to play next game.\n\n" + 202 | "You have 25 seconds to submit your vote!", 203 | new String[][]{ 204 | {MessageUtils.cHighlight2 + "Serpulo"}, 205 | {MessageUtils.cHighlight3 + "Erekir"}, 206 | {MessageUtils.cInfo + "Vote for a specific biome"}, 207 | {MessageUtils.cDanger + "Cancel"} 208 | }, 209 | new int[][]{ 210 | {-1}, 211 | {-1}, 212 | {21}, 213 | {-1} 214 | }, 215 | new MenuUtils.Handler[][]{ 216 | {p -> { 217 | if (!biomeVoteFinished) { 218 | for (int i = 0; i < Biomes.all.size(); i++) { 219 | Biomes.Biome biome = Biomes.all.get(i); 220 | if (biome.getPlanet().equals(Planets.serpulo.name)) { 221 | biomeVotes.put(biome, biomeVotes.getOrDefault(biome, 0) + 1); 222 | } 223 | } 224 | MessageUtils.sendMessage(player, "Voted for " + MessageUtils.cInfo + "Serpulo" + MessageUtils.cDefault, MessageUtils.MessageLevel.INFO); 225 | } else { 226 | MessageUtils.sendMessage(player, "Biome vote over!", MessageUtils.MessageLevel.WARNING); 227 | } 228 | }}, 229 | {p -> { 230 | if (!biomeVoteFinished) { 231 | for (int i = 0; i < Biomes.all.size(); i++) { 232 | Biomes.Biome biome = Biomes.all.get(i); 233 | if (biome.getPlanet().equals(Planets.erekir.name)) { 234 | biomeVotes.put(biome, biomeVotes.getOrDefault(biome, 0) + 1); 235 | } 236 | } 237 | MessageUtils.sendMessage(player, "Voted for " + MessageUtils.cInfo + "Erekir" + MessageUtils.cDefault, MessageUtils.MessageLevel.INFO); 238 | } else { 239 | MessageUtils.sendMessage(player, "Biome vote over!", MessageUtils.MessageLevel.WARNING); 240 | } 241 | }}, 242 | {p -> { 243 | 244 | }}, 245 | {p -> { 246 | 247 | }} 248 | } 249 | ); 250 | }); 251 | 252 | MenuUtils.addMenu(21, player -> { 253 | String[][] options = new String[Biomes.all.size() + 1][1]; 254 | int[][] links = new int[Biomes.all.size() + 1][1]; 255 | MenuUtils.Handler[][] handlers = new MenuUtils.Handler[Biomes.all.size() + 1][1]; 256 | 257 | final int[] i = {0}; 258 | Biomes.all.stream().filter(e -> e.getPlanet().equals(Planets.serpulo.name)).sorted((e1, e2) -> e1.toString().compareToIgnoreCase(e2.toString())).forEach(biome -> { 259 | options[i[0]][0] = (biome.getPlanet().equals(Planets.serpulo.name) ? MessageUtils.cHighlight2 : MessageUtils.cHighlight3) + biome; 260 | links[i[0]][0] = -1; 261 | handlers[i[0]][0] = p -> { 262 | if (!biomeVoteFinished) { 263 | biomeVotes.put(biome, biomeVotes.getOrDefault(biome, 0) + 1); 264 | MessageUtils.sendMessage(player, "Voted for " + MessageUtils.cInfo + biome + MessageUtils.cDefault, MessageUtils.MessageLevel.INFO); 265 | } else { 266 | MessageUtils.sendMessage(player, "Biome vote over!", MessageUtils.MessageLevel.WARNING); 267 | } 268 | }; 269 | 270 | i[0]++; 271 | }); 272 | 273 | Biomes.all.stream().filter(e -> e.getPlanet().equals(Planets.erekir.name)).sorted((e1, e2) -> e1.toString().compareToIgnoreCase(e2.toString())).forEach(biome -> { 274 | options[i[0]][0] = (biome.getPlanet().equals(Planets.serpulo.name) ? MessageUtils.cHighlight2 : MessageUtils.cHighlight3) + biome; 275 | links[i[0]][0] = -1; 276 | handlers[i[0]][0] = p -> { 277 | if (!biomeVoteFinished) { 278 | biomeVotes.put(biome, biomeVotes.getOrDefault(biome, 0) + 1); 279 | MessageUtils.sendMessage(player, "Voted for " + MessageUtils.cInfo + biome + MessageUtils.cDefault, MessageUtils.MessageLevel.INFO); 280 | } else { 281 | MessageUtils.sendMessage(player, "Biome vote over!", MessageUtils.MessageLevel.WARNING); 282 | } 283 | }; 284 | 285 | i[0]++; 286 | }); 287 | 288 | options[Biomes.all.size()][0] = MessageUtils.cDanger + "Cancel"; 289 | links[Biomes.all.size()][0] = -1; 290 | handlers[Biomes.all.size()][0] = p -> { 291 | 292 | }; 293 | 294 | return new MenuUtils.MenuContent( 295 | "GAME OVER - Biome Vote", 296 | (State.winner == null ? MessageUtils.cDanger + "Crux[white] won the game in " + MessageUtils.cInfo + Vars.state.wave + "[white] waves!\n\n" : MessageUtils.cPlayer + State.winner.player.name + "[white] won the game in " + MessageUtils.cInfo + Vars.state.wave + "[white] waves!\n\n") + 297 | "Vote for a biome you want to play next game.\n\n" + 298 | "You have 25 seconds to submit your vote!", 299 | options, 300 | links, 301 | handlers 302 | ); 303 | }); 304 | 305 | Events.on(SectorizedEvents.RestartEvent.class, event -> { 306 | State.gameState = State.GameState.GAMEOVER; 307 | 308 | Log.info("Restarting: " + event.reason); 309 | MessageUtils.sendMessage(event.reason + "\nServer is restarting in " + MessageUtils.cInfo + "30" + MessageUtils.cDefault + " seconds", MessageUtils.MessageLevel.INFO); 310 | 311 | for (Player player : Groups.player) { 312 | MenuUtils.showMenu(20, player); 313 | 314 | Timer.schedule(() -> { 315 | if (biomeVotes.isEmpty() || biomeVoteFinished) return; 316 | 317 | StringBuilder votes = new StringBuilder(); 318 | int maxValueInMap = (Collections.max(biomeVotes.values())); 319 | 320 | if (biomeVotes.entrySet().stream().anyMatch(e -> e.getKey().getPlanet().equals(Planets.serpulo.name))) 321 | votes.append(MessageUtils.cHighlight2 + "\n\nSerpulo"); 322 | biomeVotes.entrySet().stream().filter(e -> e.getKey().getPlanet().equals(Planets.serpulo.name)).sorted((e1, e2) -> e1.getKey().toString().compareToIgnoreCase(e2.getKey().toString())).forEach(e -> { 323 | votes.append("\n") 324 | .append(e.getValue() == maxValueInMap ? MessageUtils.cInfo : MessageUtils.cDefault) 325 | .append(e.getKey()) 326 | .append(" (") 327 | .append(e.getValue()) 328 | .append(")") 329 | .append(MessageUtils.cDefault); 330 | }); 331 | 332 | if (biomeVotes.entrySet().stream().anyMatch(e -> e.getKey().getPlanet().equals(Planets.erekir.name))) 333 | votes.append(MessageUtils.cHighlight3 + "\n\nErekir"); 334 | biomeVotes.entrySet().stream().filter(e -> e.getKey().getPlanet().equals(Planets.erekir.name)).sorted((e1, e2) -> e1.getKey().toString().compareToIgnoreCase(e2.getKey().toString())).forEach(e -> { 335 | votes.append("\n") 336 | .append(e.getValue() == maxValueInMap ? MessageUtils.cInfo : MessageUtils.cDefault) 337 | .append(e.getKey()) 338 | .append(" (") 339 | .append(e.getValue()) 340 | .append(")") 341 | .append(MessageUtils.cDefault); 342 | }); 343 | 344 | Call.infoPopupReliable(MessageUtils.cHighlight1 + "Votes:" + MessageUtils.cDefault + votes, 3.01f, Align.topLeft, 90, 5, 0, 0); 345 | }, 0, 3); 346 | } 347 | 348 | Timer.schedule(() -> { 349 | if (!biomeVotes.isEmpty()) { 350 | Seq> maxEntries = new Seq<>(); 351 | int maxValueInMap = (Collections.max(biomeVotes.values())); 352 | for (Map.Entry entry : biomeVotes.entrySet()) { 353 | if (entry.getValue() == maxValueInMap) { 354 | maxEntries.add(entry); 355 | } 356 | } 357 | Map.Entry voteWinnerBiomeEntry = maxEntries.random(); 358 | 359 | MessageUtils.sendMessage(MessageUtils.cHighlight1 + voteWinnerBiomeEntry.getKey() + " (" + Strings.capitalize(voteWinnerBiomeEntry.getKey().getPlanet()) + ")" + MessageUtils.cDefault + " won with " + MessageUtils.cHighlight2 + voteWinnerBiomeEntry.getValue() + MessageUtils.cDefault + " vote(s)!", MessageUtils.MessageLevel.INFO); 360 | Call.announce(MessageUtils.cHighlight1 + voteWinnerBiomeEntry.getKey() + " (" + Strings.capitalize(voteWinnerBiomeEntry.getKey().getPlanet()) + ")" + MessageUtils.cDefault + " won with " + MessageUtils.cHighlight2 + voteWinnerBiomeEntry.getValue() + MessageUtils.cDefault + " vote(s)!"); 361 | 362 | Core.settings.put("biomeVote", voteWinnerBiomeEntry.getKey().toString()); 363 | } else { 364 | Core.settings.put("biomeVote", ""); 365 | } 366 | Core.settings.manualSave(); 367 | biomeVoteFinished = true; 368 | }, 20); 369 | 370 | final int seconds = 10; 371 | AtomicInteger countdown = new AtomicInteger(seconds); 372 | Timer.schedule(() -> { 373 | if (countdown.get() == 0) { 374 | Log.info("Restarting server ..."); 375 | netServer.kickAll(Packets.KickReason.serverRestarting); 376 | 377 | Events.fire(new SectorizedEvents.ShutdownEvent()); 378 | 379 | System.exit(1); 380 | } 381 | 382 | MessageUtils.sendMessage("Server is restarting in " + MessageUtils.cInfo + (countdown.getAndDecrement()) + MessageUtils.cDefault + " second(s).", MessageUtils.MessageLevel.INFO); 383 | }, 25, 1, seconds); 384 | }); 385 | } 386 | 387 | @Override 388 | public void reset() { 389 | State.time = 0; 390 | 391 | interval.clear(); 392 | hideHud.clear(); 393 | } 394 | 395 | @Override 396 | public void registerServerCommands(CommandHandler handler) { 397 | handler.register("restart", "Restarts the server.", args -> { 398 | DiscordBot.sendMessage("**Game Over!** Restart called from console"); 399 | 400 | Events.fire(new SectorizedEvents.RestartEvent("Called from console")); 401 | }); 402 | } 403 | 404 | @Override 405 | public void registerClientCommands(CommandHandler handler) { 406 | handler.register("hud", "Toggles the visibility of the hud.", (args, player) -> { 407 | if (State.gameState == State.GameState.INACTIVE || State.gameState == State.GameState.GAMEOVER) return; 408 | 409 | String uuid = player.uuid(); 410 | if (hideHud.contains(uuid)) { 411 | hideHud.remove(uuid); 412 | MessageUtils.sendMessage(player, "Hud will be shown again shortly!", MessageUtils.MessageLevel.INFO); 413 | } else { 414 | hideHud.add(uuid); 415 | MessageUtils.sendMessage(player, "Hud will be hidden shortly!", MessageUtils.MessageLevel.INFO); 416 | } 417 | }); 418 | 419 | handler.register("restart", "Restarts the server, only available if only one player is online.", (args, player) -> { 420 | if (State.gameState == State.GameState.INACTIVE || State.gameState == State.GameState.GAMEOVER) return; 421 | 422 | int sec = (int) (State.time / 60); 423 | if (sec < 30) { 424 | MessageUtils.sendMessage(player, "You cannot call a restart at the start of a game, please wait " + MessageUtils.cInfo + "30 seconds" + MessageUtils.cDefault + "!", MessageUtils.MessageLevel.WARNING); 425 | return; 426 | } 427 | 428 | if (Groups.player.size() == 1) { 429 | DiscordBot.sendMessage("**Game Over!** Restart called by player *" + player.name + "*"); 430 | 431 | Events.fire(new SectorizedEvents.RestartEvent("Called by player")); 432 | } else { 433 | MessageUtils.sendMessage(player, "You can only call a restart if no one else is online.", MessageUtils.MessageLevel.WARNING); 434 | } 435 | }); 436 | 437 | handler.register("time", "Display elapsed time.", (args, player) -> { 438 | if (State.gameState == State.GameState.INACTIVE || State.gameState == State.GameState.GAMEOVER) return; 439 | 440 | int hour = (int) (State.time / 60 / 60 / 60); 441 | int min = (int) (State.time / 60 / 60 % 60); 442 | int sec = (int) (State.time / 60 % 60); 443 | 444 | MessageUtils.sendMessage(player, "Elapsed time: " + 445 | (hour > 0 ? hour + "h " : "") + 446 | (min > 0 ? min + "m " : "") + 447 | sec + "s", MessageUtils.MessageLevel.INFO); 448 | }); 449 | } 450 | 451 | private void setServerDescription() { 452 | if (state.wave < 2) { 453 | DiscordBot.setStatus("Wave " + state.wave + " | Time elapsed: Just started! | Players: " + Groups.player.size()); 454 | 455 | Administration.Config.desc.set("[white]Wave [red]" + state.wave + "[gray] |[white] Time elapsed: [green]Just started!"); 456 | } else { 457 | int hour = (int) (State.time / 60 / 60 / 60); 458 | int min = (int) (State.time / 60 / 60 % 60); 459 | 460 | DiscordBot.setStatus("Wave " + state.wave + " | Time elapsed: " + (hour > 0 ? hour + "h " : "") + min + "m" + (State.gameState == State.GameState.LOCKED ? " | LOCKED" : "") + " | Players: " + Groups.player.size()); 461 | 462 | Administration.Config.desc.set("[white]Wave [red]" + state.wave + "[gray] |[white] Time elapsed: [goldenrod]" + (hour > 0 ? hour + "h " : "") + 463 | min + "m" + (State.gameState == State.GameState.LOCKED ? "[gray] | [purple]LOCKED" : "")); 464 | } 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /src/sectorized/world/WorldManager.java: -------------------------------------------------------------------------------- 1 | package sectorized.world; 2 | 3 | import arc.Events; 4 | import arc.util.CommandHandler; 5 | import sectorized.Manager; 6 | import sectorized.SectorizedEvents; 7 | import sectorized.constant.Constants; 8 | import sectorized.world.map.MapGenerator; 9 | 10 | import static mindustry.Vars.world; 11 | 12 | public class WorldManager implements Manager { 13 | @Override 14 | public void init() { 15 | Events.on(SectorizedEvents.GamemodeStartEvent.class, event -> { 16 | MapGenerator mapGenerator = new MapGenerator(); 17 | world.loadGenerator(Constants.mapWidth, Constants.mapHeight, mapGenerator); 18 | }); 19 | } 20 | 21 | @Override 22 | public void reset() { 23 | 24 | } 25 | 26 | @Override 27 | public void registerServerCommands(CommandHandler handler) { 28 | 29 | } 30 | 31 | @Override 32 | public void registerClientCommands(CommandHandler handler) { 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/sectorized/world/map/Biomes.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map; 2 | 3 | import mindustry.world.Tile; 4 | import sectorized.world.map.biomes.erekir.*; 5 | import sectorized.world.map.biomes.serpulo.*; 6 | 7 | import java.util.ArrayList; 8 | 9 | public class Biomes { 10 | public static Biome savanna, vulcano, swamp, tundra, desert, salines, grove, ruins, archipelago; 11 | 12 | public static Biome crystal, beryllic, arkyic, regolith, rhyolite, carbon, redstone; 13 | 14 | public static ArrayList all = new ArrayList<>(); 15 | 16 | static { 17 | savanna = new Savanna(); 18 | all.add(savanna); 19 | vulcano = new Vulcano(); 20 | all.add(vulcano); 21 | swamp = new Swamp(); 22 | all.add(swamp); 23 | tundra = new Tundra(); 24 | all.add(tundra); 25 | desert = new Desert(); 26 | all.add(desert); 27 | salines = new Salines(); 28 | all.add(salines); 29 | grove = new Grove(); 30 | all.add(grove); 31 | ruins = new Ruins(); 32 | all.add(ruins); 33 | archipelago = new Archipelago(); 34 | all.add(archipelago); 35 | 36 | crystal = new Crystal(); 37 | all.add(crystal); 38 | beryllic = new Beryllic(); 39 | all.add(beryllic); 40 | arkyic = new Arkyic(); 41 | all.add(arkyic); 42 | regolith = new Regolith(); 43 | all.add(regolith); 44 | rhyolite = new Rhyolite(); 45 | all.add(rhyolite); 46 | carbon = new Carbon(); 47 | all.add(carbon); 48 | redstone = new Redstone(); 49 | all.add(redstone); 50 | } 51 | 52 | public interface Biome { 53 | void sample(int x, int y, Tile tile, Biome neighbor, double proximity); 54 | 55 | String getPlanet(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/sectorized/world/map/BiomesGenerator.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map; 2 | 3 | import sectorized.world.map.generator.BiomeSelection; 4 | 5 | public interface BiomesGenerator { 6 | BiomeSelection sample(int x, int y); 7 | } 8 | -------------------------------------------------------------------------------- /src/sectorized/world/map/ErekirBiomesGenerator.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map; 2 | 3 | import sectorized.world.map.generator.BiomeSelection; 4 | import sectorized.world.map.generator.SimplexGenerator3D; 5 | 6 | public class ErekirBiomesGenerator implements BiomesGenerator { 7 | private final SimplexGenerator3D generator; 8 | 9 | public ErekirBiomesGenerator() { 10 | generator = new SimplexGenerator3D(new Biomes.Biome[][][]{ 11 | { 12 | {Biomes.carbon, Biomes.carbon, Biomes.carbon}, 13 | {Biomes.arkyic, Biomes.arkyic, Biomes.crystal} 14 | }, 15 | { 16 | {Biomes.crystal, Biomes.rhyolite, Biomes.carbon}, 17 | {Biomes.redstone, Biomes.beryllic, Biomes.carbon} 18 | }, 19 | { 20 | {Biomes.regolith, Biomes.crystal, Biomes.redstone}, 21 | {Biomes.regolith, Biomes.rhyolite, Biomes.rhyolite}, 22 | } 23 | }, 10, 0.32, 0.005, 2.0); 24 | } 25 | 26 | public BiomeSelection sample(int x, int y) { 27 | return generator.sample(x, y); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/sectorized/world/map/MapGenerator.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map; 2 | 3 | import arc.Core; 4 | import arc.Events; 5 | import arc.func.Cons; 6 | import arc.math.Mathf; 7 | import arc.struct.StringMap; 8 | import mindustry.content.Blocks; 9 | import mindustry.content.Planets; 10 | import mindustry.maps.Map; 11 | import mindustry.world.Block; 12 | import mindustry.world.Tile; 13 | import mindustry.world.Tiles; 14 | import mindustry.world.blocks.environment.Floor; 15 | import sectorized.SectorizedEvents; 16 | import sectorized.constant.DiscordBot; 17 | import sectorized.constant.Loadout; 18 | import sectorized.constant.State; 19 | import sectorized.world.map.generator.BiomeSelection; 20 | 21 | import java.util.HashMap; 22 | 23 | import static mindustry.Vars.state; 24 | import static mindustry.Vars.world; 25 | 26 | public class MapGenerator implements Cons { 27 | private static final int sampleDensity = 50; 28 | 29 | private final Biomes.Biome biomeVote; 30 | 31 | public MapGenerator() { 32 | String biomeVoteString = (String) Core.settings.get("biomeVote", ""); 33 | biomeVote = Biomes.all.stream().filter(biome -> biome.toString().equals(biomeVoteString)).findFirst().orElse(null); 34 | Core.settings.put("biomeVote", ""); 35 | } 36 | 37 | @Override 38 | public void get(Tiles tiles) { 39 | final String planet = biomeVote != null ? biomeVote.getPlanet() : Mathf.chance(0.7) ? Planets.serpulo.name : Planets.erekir.name; 40 | State.planet = planet; 41 | state.rules.loadout = Loadout.getLoadout(1); 42 | 43 | RiversGenerator riversGenerator = new RiversGenerator(); 44 | BiomesGenerator biomesGenerator = planet.equals(Planets.serpulo.name) ? new SerpuloBiomesGenerator() : new ErekirBiomesGenerator(); 45 | 46 | int offsetX = Mathf.random(9999999); 47 | int offsetY = Mathf.random(9999999); 48 | 49 | HashMap biomeDistribution = new HashMap<>(); 50 | 51 | if (biomeVote != null) { 52 | int maxOccurrences = 0; 53 | int maxOffsetX = offsetX, maxOffsetY = offsetY; 54 | 55 | for (int i = 0; i < 10; i++) { 56 | for (int x = sampleDensity / 2; x < world.width(); x += sampleDensity) { 57 | for (int y = sampleDensity / 2; y < world.height(); y += sampleDensity) { 58 | Biomes.Biome biome = biomesGenerator.sample(x + offsetX, y + offsetY).closest; 59 | 60 | biomeDistribution.put(biome, biomeDistribution.getOrDefault(biome, 0) + 1); 61 | } 62 | } 63 | 64 | int occurrences = biomeDistribution.getOrDefault(biomeVote, 0); 65 | 66 | if (occurrences > maxOccurrences) { 67 | maxOccurrences = occurrences; 68 | maxOffsetX = offsetX; 69 | maxOffsetY = offsetY; 70 | } 71 | 72 | maxOccurrences = Math.max(maxOccurrences, occurrences); 73 | 74 | offsetX = Mathf.random(9999999); 75 | offsetY = Mathf.random(9999999); 76 | 77 | biomeDistribution.clear(); 78 | } 79 | 80 | offsetX = maxOffsetX; 81 | offsetY = maxOffsetY; 82 | } 83 | 84 | for (int x = 0; x < world.width(); x++) { 85 | for (int y = 0; y < world.height(); y++) { 86 | if (planet.equals(Planets.serpulo.name)) { 87 | Block water = riversGenerator.sample(x + offsetX, y + offsetY); 88 | 89 | Tile tile = new Tile(x, y); 90 | 91 | if (x == 0 || x == world.width() - 1 || y == 0 || y == world.height() - 1) { 92 | tile.setFloor((Floor) Blocks.stone); 93 | tile.setBlock(Blocks.duneWall); 94 | } else { 95 | if (water == null) { 96 | BiomeSelection biomeSelection = biomesGenerator.sample(x + offsetX, y + offsetY); 97 | 98 | biomeDistribution.put(biomeSelection.closest, biomeDistribution.getOrDefault(biomeSelection.closest, 0) + 1); 99 | 100 | biomeSelection.closest.sample(x + offsetX, y + offsetY, tile, biomeSelection.farthest, biomeSelection.proximity); 101 | } else { 102 | tile.setFloor((Floor) water); 103 | } 104 | } 105 | 106 | tiles.set(x, y, tile); 107 | } else if (planet.equals(Planets.erekir.name)) { 108 | Tile tile = new Tile(x, y); 109 | 110 | if (x <= 1 || x >= world.width() - 2 || y <= 1 || y >= world.height() - 2) { 111 | tile.setFloor((Floor) Blocks.carbonStone); 112 | tile.setBlock(Blocks.carbonWall); 113 | } else { 114 | BiomeSelection biomeSelection = biomesGenerator.sample(x + offsetX, y + offsetY); 115 | 116 | biomeDistribution.put(biomeSelection.closest, biomeDistribution.getOrDefault(biomeSelection.closest, 0) + 1); 117 | 118 | biomeSelection.closest.sample(x + offsetX, y + offsetY, tile, biomeSelection.farthest, biomeSelection.proximity); 119 | } 120 | 121 | tiles.set(x, y, tile); 122 | } 123 | } 124 | } 125 | 126 | StringBuilder mostFrequentBiomes = new StringBuilder(); 127 | int threshold = (int) (world.width() * world.height() * 0.1); 128 | final int[] count = {0}; 129 | 130 | biomeDistribution.forEach((key, value) -> { 131 | if (value >= threshold && count[0] < 3) { 132 | mostFrequentBiomes.append(key.toString()).append("-"); 133 | count[0]++; 134 | } 135 | }); 136 | 137 | mostFrequentBiomes.deleteCharAt(mostFrequentBiomes.length() - 1); 138 | 139 | Events.fire(new SectorizedEvents.BiomesGeneratedEvent()); 140 | 141 | DiscordBot.sendMessage("**Server started!** Current map: " + mostFrequentBiomes); 142 | 143 | if (planet.equals(Planets.serpulo.name)) { 144 | state.rules.env = Planets.serpulo.defaultEnv; 145 | state.rules.hiddenBuildItems.clear(); 146 | state.rules.hiddenBuildItems.addAll(Planets.serpulo.hiddenItems); 147 | } else if (planet.equals(Planets.erekir.name)) { 148 | state.rules.env = Planets.erekir.defaultEnv; 149 | state.rules.hiddenBuildItems.clear(); 150 | state.rules.hiddenBuildItems.addAll(Planets.erekir.hiddenItems); 151 | } 152 | 153 | state.map = new Map(StringMap.of("name", mostFrequentBiomes)); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/sectorized/world/map/RiversGenerator.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map; 2 | 3 | import arc.math.Mathf; 4 | import arc.util.noise.Simplex; 5 | import mindustry.content.Blocks; 6 | import mindustry.world.Block; 7 | import sectorized.world.map.generator.BlockG; 8 | import sectorized.world.map.generator.Generator; 9 | import sectorized.world.map.generator.SimplexGenerator2D; 10 | 11 | public class RiversGenerator { 12 | private final SimplexGenerator2D generator; 13 | private final int seed = Mathf.random(99999999); 14 | 15 | public RiversGenerator() { 16 | generator = new SimplexGenerator2D(new Generator[][]{ 17 | {BlockG.placeholder, BlockG.water, BlockG.deepwater, BlockG.water, BlockG.placeholder}, 18 | {BlockG.water, BlockG.water, BlockG.water, BlockG.water, BlockG.water}, 19 | {BlockG.deepwater, BlockG.water, BlockG.water, BlockG.deepwater, BlockG.deepwater}, 20 | {BlockG.water, BlockG.water, BlockG.deepwater, BlockG.deepwater, BlockG.water}, 21 | {BlockG.placeholder, BlockG.water, BlockG.deepwater, BlockG.water, BlockG.placeholder}, 22 | }, 12, 0.5, 0.0008, 32, 12, 0.5, 0.0008, 32); 23 | } 24 | 25 | public Block sample(int x, int y) { 26 | Block sample = generator.sample(x, y); 27 | 28 | if (sample == Blocks.deepwater) { 29 | double gap = Simplex.noise2d(seed, 12, 0.5, 0.01f, x, y); 30 | 31 | if (gap < 0.4) sample = Blocks.water; 32 | } 33 | 34 | return sample; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/sectorized/world/map/SerpuloBiomesGenerator.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map; 2 | 3 | import sectorized.world.map.generator.BiomeSelection; 4 | import sectorized.world.map.generator.SimplexGenerator3D; 5 | 6 | public class SerpuloBiomesGenerator implements BiomesGenerator { 7 | private final SimplexGenerator3D generator; 8 | 9 | public SerpuloBiomesGenerator() { 10 | generator = new SimplexGenerator3D(new Biomes.Biome[][][]{ 11 | { 12 | {Biomes.desert, Biomes.desert, Biomes.archipelago}, 13 | {Biomes.desert, Biomes.desert, Biomes.ruins}, 14 | {Biomes.grove, Biomes.desert, Biomes.salines} 15 | }, 16 | { 17 | {Biomes.desert, Biomes.desert, Biomes.swamp}, 18 | {Biomes.desert, Biomes.desert, Biomes.swamp}, 19 | {Biomes.vulcano, Biomes.vulcano, Biomes.tundra} 20 | }, 21 | { 22 | {Biomes.savanna, Biomes.savanna, Biomes.swamp}, 23 | {Biomes.savanna, Biomes.ruins, Biomes.swamp}, 24 | {Biomes.vulcano, Biomes.vulcano, Biomes.tundra} 25 | } 26 | }, 12, 0.7, 0.0002, 8); 27 | } 28 | 29 | public BiomeSelection sample(int x, int y) { 30 | return generator.sample(x, y); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/ErekirBiome.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes; 2 | 3 | import arc.math.Mathf; 4 | import arc.struct.Seq; 5 | import arc.util.noise.Noise; 6 | import arc.util.noise.Simplex; 7 | import mindustry.content.Blocks; 8 | import mindustry.content.Planets; 9 | import mindustry.maps.filters.GenerateFilter; 10 | import mindustry.maps.filters.OreFilter; 11 | import mindustry.world.Block; 12 | import mindustry.world.Tile; 13 | import mindustry.world.blocks.environment.Floor; 14 | import sectorized.world.map.Biomes; 15 | import sectorized.world.map.generator.SimplexGenerator2D; 16 | 17 | import static mindustry.Vars.world; 18 | 19 | public class ErekirBiome implements Biomes.Biome { 20 | private final SimplexGenerator2D generator; 21 | 22 | protected final Seq ores; 23 | private final GenerateFilter.GenerateInput in; 24 | 25 | protected final Floor vent; 26 | 27 | private final int seed1 = Mathf.random(99999999); 28 | private final int seed2 = Mathf.random(99999999); 29 | private final int seed3 = Mathf.random(99999999); 30 | private final int seed4 = Mathf.random(99999999); 31 | private final int seed5 = Mathf.random(99999999); 32 | 33 | private final double wallThreshold = 0.6; 34 | 35 | private final double ventThreshold; 36 | 37 | private final Block wall; 38 | 39 | public ErekirBiome(SimplexGenerator2D generator, Floor vent, Block wall) { 40 | this(generator, vent, wall, 0.3); 41 | } 42 | 43 | public ErekirBiome(SimplexGenerator2D generator, Floor vent, Block wall, double ventThreshold) { 44 | this.generator = generator; 45 | this.vent = vent; 46 | this.wall = wall; 47 | this.ventThreshold = ventThreshold; 48 | 49 | ores = new Seq<>(); 50 | Seq oreBlocks = Seq.with(Blocks.oreBeryllium, Blocks.oreTungsten, Blocks.oreCrystalThorium); 51 | for (Block block : oreBlocks) { 52 | OreFilter filter = new OreFilter(); 53 | filter.threshold = block.asFloor().oreThreshold += 0.005f; 54 | filter.scl = block.asFloor().oreScale -= 1f; 55 | filter.ore = block; 56 | ores.add(filter); 57 | } 58 | ores.each(o -> { 59 | OreFilter oreFilter = ((OreFilter) o); 60 | if (oreFilter.ore == Blocks.oreBeryllium) { 61 | oreFilter.threshold -= 0.04f; 62 | oreFilter.scl -= 2f; 63 | } 64 | }); 65 | ores.each(o -> { 66 | OreFilter oreFilter = ((OreFilter) o); 67 | if (oreFilter.ore == Blocks.oreTungsten) { 68 | oreFilter.threshold -= 0.04f; 69 | oreFilter.scl -= 2f; 70 | } 71 | }); 72 | 73 | ores.each(GenerateFilter::randomize); 74 | 75 | in = new GenerateFilter.GenerateInput(); 76 | } 77 | 78 | @Override 79 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 80 | Block floor = generator.sample(x, y); 81 | 82 | tile.setFloor((Floor) floor); 83 | 84 | for (GenerateFilter f : ores) { 85 | in.floor = floor; 86 | in.block = Blocks.air; 87 | in.overlay = Blocks.air; 88 | in.x = x; 89 | in.y = y; 90 | in.width = world.width(); 91 | in.height = world.height(); 92 | f.apply(in); 93 | if (in.overlay != Blocks.air) { 94 | tile.setOverlay(in.overlay); 95 | } 96 | } 97 | 98 | boolean wall = normalizeSimplex(Simplex.noise2d(seed1, 10, 0.4, 0.03, x, y)) < 0.5 && proximity < wallThreshold; 99 | if (wall) { 100 | boolean carbon = normalizeSimplex(Simplex.noise2d(seed2, 10, 0.4, 0.05, x, y)) < 0.5; 101 | 102 | tile.setBlock(carbon ? Blocks.carbonWall : this.wall); 103 | 104 | if (proximity > wallThreshold - 0.05) { 105 | if (carbon) 106 | tile.setBlock(Blocks.graphiticWall); 107 | else if (normalizeSimplex(Simplex.noise2d(seed3, 10, 0.4, 0.05, x, y)) < 0.6) 108 | tile.setOverlay(Blocks.wallOreBeryllium); 109 | else if (normalizeSimplex(Simplex.noise2d(seed4, 10, 0.4, 0.05, x, y)) < 0.4) 110 | tile.setOverlay(Blocks.wallOreThorium); 111 | else if (normalizeSimplex(Simplex.noise2d(seed5, 10, 0.4, 0.05, x, y)) < 0.3) 112 | tile.setOverlay(Blocks.wallOreTungsten); 113 | } 114 | } else if (vent != null && ((Floor) floor).hasSurface()) { 115 | int sx = (x / 5), sy = (y / 5); 116 | int mx = x % 5, my = y % 5; 117 | 118 | double n = Noise.rawNoise(sx * Math.E, sy * Math.E); 119 | int offset = n < 0.1 ? n < -0.1 ? -1 : 0 : 1; 120 | 121 | if (mx > offset && mx < 4 + offset && my > offset && my < 4 + offset && Noise.rawNoise(sx * Math.PI, sy * Math.PI) > ventThreshold) { 122 | tile.setFloor(vent); 123 | } 124 | } 125 | } 126 | 127 | @Override 128 | public String getPlanet() { 129 | return Planets.erekir.name; 130 | } 131 | 132 | private double normalizeSimplex(double value) { 133 | return (value - 0.1) * 1.25; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/SerpuloBiome.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes; 2 | 3 | import arc.struct.Seq; 4 | import mindustry.content.Blocks; 5 | import mindustry.content.Planets; 6 | import mindustry.maps.filters.GenerateFilter; 7 | import mindustry.world.Block; 8 | import mindustry.world.Tile; 9 | import mindustry.world.blocks.environment.Floor; 10 | import sectorized.world.map.Biomes; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | import static mindustry.Vars.maps; 14 | import static mindustry.Vars.world; 15 | 16 | public class SerpuloBiome implements Biomes.Biome { 17 | private final String planet = Planets.serpulo.name; 18 | 19 | private final SimplexGenerator2D generator; 20 | 21 | protected final Seq ores; 22 | private final GenerateFilter.GenerateInput in; 23 | 24 | public SerpuloBiome(SimplexGenerator2D generator) { 25 | this.generator = generator; 26 | 27 | ores = new Seq<>(); 28 | maps.addDefaultOres(ores); 29 | ores.each(GenerateFilter::randomize); 30 | 31 | in = new GenerateFilter.GenerateInput(); 32 | } 33 | 34 | @Override 35 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 36 | Block floor = generator.sample(x, y); 37 | 38 | tile.setFloor((Floor) floor); 39 | 40 | for (GenerateFilter f : ores) { 41 | in.floor = floor; 42 | in.block = Blocks.air; 43 | in.overlay = Blocks.air; 44 | in.x = x; 45 | in.y = y; 46 | in.width = world.width(); 47 | in.height = world.height(); 48 | f.apply(in); 49 | if (in.overlay != Blocks.air) { 50 | tile.setOverlay(in.overlay); 51 | } 52 | } 53 | } 54 | 55 | @Override 56 | public String getPlanet() { 57 | return Planets.serpulo.name; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/erekir/Arkyic.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.erekir; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.world.Tile; 6 | import mindustry.world.blocks.environment.Floor; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.ErekirBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Arkyic extends ErekirBiome { 14 | public Arkyic() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.arkyicStone, BlockG.arkyicStone, BlockG.arkyicStone}, 17 | {BlockG.arkyicStone, BlockG.arkyicStone, BlockG.arkyicStone}, 18 | {BlockG.arkyicStone, BlockG.arkyicStone, BlockG.arkyciteFloor} 19 | }, 12, 0.3, 0.05, 1.0, 12, 0.3, 0.05, 1.0), (Floor) Blocks.arkyicVent, Blocks.arkyicWall); 20 | } 21 | 22 | @Override 23 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 24 | super.sample(x, y, tile, neighbor, proximity); 25 | 26 | if (tile.block() == Blocks.air) { 27 | if (tile.floor() == Blocks.arkyicStone && Mathf.chance(0.008)) tile.setBlock(Blocks.arkyicBoulder); 28 | if (tile.floor() == Blocks.arkyicStone && Mathf.chance(0.001)) tile.setBlock(Blocks.crystalOrbs); 29 | } 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "Arkyic"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/erekir/Beryllic.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.erekir; 2 | 3 | import mindustry.content.Blocks; 4 | import mindustry.world.Tile; 5 | import sectorized.world.map.Biomes; 6 | import sectorized.world.map.biomes.ErekirBiome; 7 | import sectorized.world.map.generator.BlockG; 8 | import sectorized.world.map.generator.Generator; 9 | import sectorized.world.map.generator.SimplexGenerator2D; 10 | 11 | public class Beryllic extends ErekirBiome { 12 | public Beryllic() { 13 | super(new SimplexGenerator2D(new Generator[][]{ 14 | {BlockG.arkyciteFloor, BlockG.beryllicStone, BlockG.darksand}, 15 | {BlockG.beryllicStone, BlockG.beryllicStone, BlockG.beryllicStone}, 16 | {BlockG.beryllicStone, BlockG.darksand, BlockG.beryllicStone} 17 | }, 12, 0.7, 0.006, 1.15, 12, 0.6, 0.004, 1.15), null, Blocks.beryllicStoneWall); 18 | } 19 | 20 | @Override 21 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 22 | super.sample(x, y, tile, neighbor, proximity); 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "Beryllic"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/erekir/Carbon.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.erekir; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.world.Tile; 6 | import mindustry.world.blocks.environment.Floor; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.ErekirBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Carbon extends ErekirBiome { 14 | public Carbon() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.arkyciteFloor, BlockG.carbonStone}, 17 | {BlockG.carbonStone, BlockG.carbonStone}, 18 | {BlockG.carbonStone, BlockG.carbonStone}, 19 | }, 12, 0.45, 0.04, 1.0, 12, 0.45, 0.04, 1.0), (Floor) Blocks.carbonVent, Blocks.carbonWall); 20 | } 21 | 22 | @Override 23 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 24 | super.sample(x, y, tile, neighbor, proximity); 25 | 26 | if (tile.block() == Blocks.air) { 27 | if (tile.floor() == Blocks.carbonStone && Mathf.chance(0.006)) tile.setBlock(Blocks.carbonBoulder); 28 | } 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "Carbon"; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/erekir/Crystal.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.erekir; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.world.Tile; 6 | import sectorized.world.map.Biomes; 7 | import sectorized.world.map.biomes.ErekirBiome; 8 | import sectorized.world.map.generator.BlockG; 9 | import sectorized.world.map.generator.Generator; 10 | import sectorized.world.map.generator.SimplexGenerator2D; 11 | 12 | public class Crystal extends ErekirBiome { 13 | public Crystal() { 14 | super(new SimplexGenerator2D(new Generator[][]{ 15 | {BlockG.crystallineStone, BlockG.crystalFloor, BlockG.crystallineStone}, 16 | {BlockG.crystallineStone, BlockG.crystalFloor, BlockG.crystalFloor}, 17 | {BlockG.crystalFloor, BlockG.crystalFloor, BlockG.arkyciteFloor} 18 | }, 12, 0.3, 0.05, 1.0, 12, 0.3, 0.05, 1.0), null, Blocks.carbonWall); 19 | } 20 | 21 | @Override 22 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 23 | super.sample(x, y, tile, neighbor, proximity); 24 | 25 | if (tile.block() == Blocks.air) { 26 | if (tile.floor() == Blocks.crystallineStone && Mathf.chance(0.001)) tile.setBlock(Blocks.crystalOrbs); 27 | if (tile.floor() == Blocks.crystallineStone && Mathf.chance(0.003)) 28 | tile.setBlock(Blocks.vibrantCrystalCluster); 29 | } 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "Crystal"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/erekir/Redstone.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.erekir; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.world.Tile; 6 | import mindustry.world.blocks.environment.Floor; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.ErekirBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Redstone extends ErekirBiome { 14 | public Redstone() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.redIce, BlockG.denseRedStone}, 17 | {BlockG.redStone, BlockG.denseRedStone}, 18 | {BlockG.redStone, BlockG.redIce} 19 | }, 12, 0.7, 0.05, 1.0, 12, 0.7, 0.05, 1.0), (Floor) Blocks.redStoneVent, Blocks.redStoneWall, 0.2); 20 | } 21 | 22 | @Override 23 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 24 | super.sample(x, y, tile, neighbor, proximity); 25 | 26 | if (tile.block() == Blocks.air) { 27 | if (tile.floor() == Blocks.redStone && Mathf.chance(0.008)) tile.setBlock(Blocks.redStoneBoulder); 28 | } 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "Redstone"; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/erekir/Regolith.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.erekir; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.world.Tile; 6 | import mindustry.world.blocks.environment.Floor; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.ErekirBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Regolith extends ErekirBiome { 14 | public Regolith() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.regolith, BlockG.regolith}, 17 | {BlockG.yellowStone, BlockG.yellowStonePlates} 18 | }, 12, 0.7, 0.006, 1.15, 12, 0.6, 0.004, 1.15), (Floor) Blocks.yellowStoneVent, Blocks.regolithWall); 19 | } 20 | 21 | @Override 22 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 23 | super.sample(x, y, tile, neighbor, proximity); 24 | 25 | if (tile.block() == Blocks.air) { 26 | if (tile.floor() == Blocks.yellowStone && Mathf.chance(0.008)) tile.setBlock(Blocks.yellowStoneBoulder); 27 | if (tile.floor() == Blocks.yellowStonePlates && Mathf.chance(0.005)) tile.setBlock(Blocks.yellowCoral); 28 | if (tile.floor() == Blocks.yellowStone && Mathf.chance(0.002)) tile.setBlock(Blocks.crystalBlocks); 29 | } 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "Regolith"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/erekir/Rhyolite.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.erekir; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.world.Tile; 6 | import mindustry.world.blocks.environment.Floor; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.ErekirBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Rhyolite extends ErekirBiome { 14 | public Rhyolite() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.roughRhyolite, BlockG.rhyolite, BlockG.rhyolite}, 17 | {BlockG.rhyolite, BlockG.rhyolite, BlockG.roughRhyolite}, 18 | {BlockG.roughRhyolite, BlockG.rhyoliteCrater, BlockG.slag} 19 | }, 12, 0.3, 0.05, 1.0, 12, 0.3, 0.05, 1.0), (Floor) Blocks.rhyoliteVent, Blocks.rhyoliteWall); 20 | } 21 | 22 | @Override 23 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 24 | super.sample(x, y, tile, neighbor, proximity); 25 | 26 | if (tile.block() == Blocks.air) { 27 | if (tile.floor() == Blocks.rhyolite && Mathf.chance(0.008)) tile.setBlock(Blocks.rhyoliteBoulder); 28 | if (tile.floor() == Blocks.redIce && Mathf.chance(0.01)) tile.setBlock(Blocks.redIceBoulder); 29 | } 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "Rhyolite"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/serpulo/Archipelago.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.serpulo; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.maps.filters.OreFilter; 6 | import mindustry.world.Tile; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.SerpuloBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Archipelago extends SerpuloBiome { 14 | public Archipelago() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.grass, new SimplexGenerator2D(new Generator[][]{ 17 | {BlockG.sand, BlockG.darksand, BlockG.sand}, 18 | {BlockG.sand, BlockG.tar, BlockG.sand}, 19 | {BlockG.sand, BlockG.sand, BlockG.darksand}, 20 | }, 12, 0.5, 0.03, 1.15, 12, 0.5, 0.03, 1.15), BlockG.sand, BlockG.water, BlockG.deepwater}, 21 | {BlockG.sand, BlockG.darksand, BlockG.water, BlockG.deepwater, BlockG.water}, 22 | {BlockG.darksand, BlockG.water, BlockG.deepwater, BlockG.water, BlockG.sand}, 23 | {BlockG.water, BlockG.deepwater, BlockG.water, BlockG.sand, BlockG.grass}, 24 | {BlockG.deepwater, BlockG.water, BlockG.darksand, BlockG.darksand, BlockG.grass} 25 | }, 12, 0.45, 0.01, 2, 12, 0.45, 0.01, 2)); 26 | 27 | ores.each(o -> ((OreFilter) o).scl -= 10f); 28 | ores.each(o -> ((OreFilter) o).falloff -= 0.2f); 29 | 30 | ores.each(o -> ((OreFilter) o).threshold -= 0.05f); 31 | } 32 | 33 | @Override 34 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 35 | super.sample(x, y, tile, neighbor, proximity); 36 | 37 | if (tile.block() == Blocks.air) { 38 | if (tile.floor() == Blocks.sand && Mathf.chance(0.01)) tile.setBlock(Blocks.sandBoulder); 39 | if (tile.floor() == Blocks.darksand && Mathf.chance(0.005)) tile.setBlock(Blocks.basaltBoulder); 40 | if (tile.floor() == Blocks.grass && Mathf.chance(0.01)) tile.setBlock(Blocks.pine); 41 | } 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "Archipelago"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/serpulo/Desert.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.serpulo; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.maps.filters.OreFilter; 6 | import mindustry.world.Tile; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.SerpuloBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Desert extends SerpuloBiome { 14 | public Desert() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {new SimplexGenerator2D(new Generator[][]{ 17 | {BlockG.tar, BlockG.charr}, 18 | {BlockG.stone, BlockG.tar}, 19 | }, 12, 0.7, 0.1, 1.15, 12, 0.7, 0.1, 1.15), BlockG.charr, BlockG.darksand, BlockG.darksand, BlockG.darksand}, 20 | {BlockG.stone, BlockG.darksand, BlockG.darksand, BlockG.sand, BlockG.sand}, 21 | {BlockG.darksand, BlockG.darksand, BlockG.sand, BlockG.sand, BlockG.sand}, 22 | {BlockG.darksand, BlockG.sand, BlockG.sand, BlockG.sand, BlockG.sand}, 23 | {BlockG.darksand, BlockG.sand, BlockG.sand, BlockG.sand, new SimplexGenerator2D(new Generator[][]{ 24 | {BlockG.tar, BlockG.sand}, 25 | {BlockG.tar, BlockG.shale}, 26 | }, 12, 0.5, 0.1, 1.15, 12, 0.5, 0.1, 1.15)} 27 | }, 12, 0.7, 0.006, 1.15, 12, 0.6, 0.004, 1.15)); 28 | 29 | 30 | ores.each(o -> ((OreFilter) o).threshold -= 0.03f); 31 | } 32 | 33 | @Override 34 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 35 | super.sample(x, y, tile, neighbor, proximity); 36 | 37 | if (tile.block() == Blocks.air) { 38 | if (tile.floor() == Blocks.sand && Mathf.chance(0.01)) tile.setBlock(Blocks.sandBoulder); 39 | if (tile.floor() == Blocks.darksand && Mathf.chance(0.005)) tile.setBlock(Blocks.basaltBoulder); 40 | if (tile.floor() == Blocks.grass && Mathf.chance(0.005)) tile.setBlock(Blocks.pine); 41 | } 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "Desert"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/serpulo/Grove.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.serpulo; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.maps.filters.OreFilter; 6 | import mindustry.world.Tile; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.SerpuloBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Grove extends SerpuloBiome { 14 | public Grove() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.tar, BlockG.darksand, BlockG.darksand, BlockG.shale, BlockG.dirt}, 17 | {BlockG.basalt, BlockG.shale, BlockG.shale, BlockG.dirt, BlockG.shale}, 18 | {BlockG.charr, BlockG.shale, BlockG.darksand, BlockG.shale, BlockG.darksand}, 19 | {BlockG.shale, BlockG.darksand, BlockG.shale, BlockG.darksand, BlockG.hotrock}, 20 | {BlockG.stone, new SimplexGenerator2D(new Generator[][]{ 21 | {BlockG.tar, BlockG.shale}, 22 | {BlockG.stone, BlockG.darksand}, 23 | }, 12, 0.7, 0.1, 1.15, 12, 0.7, 0.1, 1.15), BlockG.darksand, BlockG.hotrock, BlockG.magmarock} 24 | }, 12, 0.65, 0.02, 1.1, 12, 0.65, 0.002, 1.2)); 25 | 26 | ores.each(o -> ((OreFilter) o).threshold -= 0.01f); 27 | ores.each(o -> { 28 | OreFilter oreFilter = ((OreFilter) o); 29 | if (oreFilter.ore == Blocks.oreThorium) oreFilter.threshold -= 0.02f; 30 | if (oreFilter.ore == Blocks.oreTitanium) oreFilter.threshold -= 0.02f; 31 | }); 32 | ores.insert(0, new OreFilter() {{ 33 | ore = Blocks.oreScrap; 34 | scl *= 0.8f; 35 | threshold = 0.75f; 36 | }}); 37 | } 38 | 39 | @Override 40 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 41 | super.sample(x, y, tile, neighbor, proximity); 42 | 43 | if (tile.block() == Blocks.air) { 44 | if (tile.floor() == Blocks.sand && Mathf.chance(0.01)) tile.setBlock(Blocks.sandBoulder); 45 | if (tile.floor() == Blocks.shale && Mathf.chance(0.02)) tile.setBlock(Blocks.shaleBoulder); 46 | if (tile.floor() == Blocks.salt && Mathf.chance(0.001)) tile.setBlock(Blocks.boulder); 47 | if (tile.floor() == Blocks.darksand && Mathf.chance(0.005)) tile.setBlock(Blocks.basaltBoulder); 48 | } 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "Grove"; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/serpulo/Ruins.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.serpulo; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.game.Schematic; 6 | import mindustry.game.Schematics; 7 | import mindustry.game.Team; 8 | import mindustry.maps.filters.OreFilter; 9 | import mindustry.world.Tile; 10 | import sectorized.world.map.Biomes; 11 | import sectorized.world.map.biomes.SerpuloBiome; 12 | import sectorized.world.map.generator.BlockG; 13 | import sectorized.world.map.generator.Generator; 14 | import sectorized.world.map.generator.SimplexGenerator2D; 15 | 16 | import static mindustry.Vars.world; 17 | 18 | public class Ruins extends SerpuloBiome { 19 | private static final Schematic[] ruins = { 20 | Schematics.readBase64("bXNjaAF4nEWMSwqAMBBD31h/6MaLuPBIRYsUaitV8fqOuHAR8iEJLaagjHZz1Pny8ZjoF3fM2e+nTxEMnTq7j7cNgeHXY7B5dZRLShloFBQYQVTIO1TSb+gQofrS+u18bfMAAasVAw=="), 21 | Schematics.readBase64("bXNjaAF4nC1N2wrDIAw9VdlG21/xpX+UrXkQnEp0lP39ElxIci7hEAR4h1DozbjJJ5V+YDu5vyS1kWoBAlZV1OJFOWPrNZPERoUz7k8ag+WLtdWLJZZ6MoAds5b/aDljxvWZEW/LGTgzvcLymFJdWFvCz7yd8QP0RxiP"), 22 | Schematics.readBase64("bXNjaAF4nEWJ3QqEIBSExx8qdgl6Ea96IlFZDpjWSen1s7zYgWG+mYGCltDJbgEDV0rniq8Pp2PaC+UEaIwuJ1+pYGmz3c1lYzTR8i/g818wRzoqecO5lsAAJnSJ16IDZAv1oOzX04VqIXtr3w28thuS"), 23 | Schematics.readBase64("bXNjaAF4nCWM6wqDMAyFT+ttOGTsQfpvL1RsYAFtXWwne/ulSkjOyZcLevQWbfQroZfCcX/hHmifhbfMKQIDxi0dJC6mQJi2suzk5hRD4Yxp4U/h4CSVTIJHfifhsjohP+ckeGbOPlaiF1/6KRr1t9/c4ZcFD1YomYLbk9QHwKQJczNoYK2BqZ1FNcbgNA1qmFZLh6ZDq9BW6ap0V6cetr8WMJygvbhO/jfcL+E="), 24 | Schematics.readBase64("bXNjaAF4nEWKQQ7DIAwE10CrNrnkIxz7ICuxKiQCyLTK94u5VBrL410jIDiEwqfgrt9U+gvrIX3X1D6pFuAGz7pjGxG3eHHOMbO+BWuvQ2LjIhnLv8bS6iUaSz0EwHMMaAJnkKk3B7l5z8bBDywMY9HDMvuiH4ZMHPs="), 25 | Schematics.readBase64("bXNjaAF4nB2IQQ7CMAwEN26ACi58pFf+Y7mRail1KieA+D2Gw+7OLDIyIRvvBWd/qvUHbmvp4noMbQZkXMP4WN5cK+57kY1NheuyusZz6cJjFMcszV7l0xzAKYIESlEB9GOkCdPfYwj0BbRvGcI="), 26 | Schematics.readBase64("bXNjaAF4nCWLWQ4CIRBECxrUaKIn4dvzEIY4bZiWsGSub49+VF5qg4e3cBK3jFObLP2J25J7alwHfwTwuKqLNeyxFFzeU9KvcGvkgvtLq5VHDrXl3vGokucWB6ewNNYDcFbBgIzCgg5HfziQxRETKQysBjqxXzkAHfM="), 27 | Schematics.readBase64("bXNjaAF4nC2Maw7CIBCER+mD1Bi9CP+9DtJVMRSahbbp7V3UnWTmy2Sy6NEe0UQ7ETpefMw3nEbKjv1cfIqAxnUi97LROxvMyD4EaJfiSnti6PcS3Xd4yT546U2eKBRi9HdbJHcMc9qITUwjYZDPdjablS/ntBI/QtrM0xYCMOB3B9Gxwt+qpFI1FBpBsUYQbQUN1QkpqB5d3X0AZwstDw==") 28 | }; 29 | 30 | public Ruins() { 31 | super(new SimplexGenerator2D(new Generator[][]{ 32 | {new SimplexGenerator2D(new Generator[][]{ 33 | {BlockG.dirt, BlockG.cryofluid}, 34 | {BlockG.basalt, BlockG.darksand}, 35 | }, 12, 0.5, 0.1, 1.15, 12, 0.5, 0.1, 1.15), BlockG.basalt, BlockG.darksand, BlockG.sand, BlockG.grass}, 36 | {BlockG.basalt, BlockG.darksand, BlockG.darksand, BlockG.sand, BlockG.sand}, 37 | {BlockG.darksand, BlockG.darksand, BlockG.darksand, BlockG.darksand, BlockG.darksand}, 38 | {BlockG.darksand, BlockG.darksand, BlockG.darksand, new SimplexGenerator2D(new Generator[][]{ 39 | {BlockG.darksand, BlockG.darksand, BlockG.darksand}, 40 | {BlockG.tar, BlockG.darksand, BlockG.darksand}, 41 | {BlockG.darksand, BlockG.darksand, BlockG.basalt}, 42 | }, 12, 0.5, 0.1, 1.15, 12, 0.5, 0.1, 1.15), BlockG.metalFloorDamaged}, 43 | {BlockG.dacite, BlockG.darksand, BlockG.darksand, BlockG.metalFloorDamaged, BlockG.metalFloor} 44 | }, 12, 0.65, 0.02, 1.8, 12, 0.65, 0.02, 1.8)); 45 | 46 | ores.each(o -> ((OreFilter) o).threshold -= 0.01f); 47 | } 48 | 49 | @Override 50 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 51 | super.sample(x, y, tile, neighbor, proximity); 52 | 53 | if (tile.block() == Blocks.air) { 54 | if (tile.floor() == Blocks.sand && Mathf.chance(0.01)) tile.setBlock(Blocks.sandBoulder); 55 | if (tile.floor() == Blocks.salt && Mathf.chance(0.002)) tile.setBlock(Blocks.boulder); 56 | if (tile.floor() == Blocks.darksand && Mathf.chance(0.005)) tile.setBlock(Blocks.basaltBoulder); 57 | if (tile.floor() == Blocks.shale && Mathf.chance(0.005)) tile.setBlock(Blocks.pine); 58 | } 59 | 60 | if (tile.floor() == Blocks.metalFloor || tile.floor() == Blocks.metalFloorDamaged) { 61 | if (Mathf.chance(0.05)) setRandomRuin(tile); 62 | } 63 | 64 | if (tile.floor() == Blocks.darksand) { 65 | if (Mathf.chance(0.005)) setRandomRuin(tile); 66 | } 67 | } 68 | 69 | public void setRandomRuin(Tile tile) { 70 | Schematic ruin = Schematics.rotate(ruins[Mathf.random(ruins.length - 1)], Mathf.random(3)); 71 | 72 | ruin.tiles.each(st -> { 73 | Tile t = world.tile(tile.x + st.x - ruin.width, tile.y + st.y - ruin.height); 74 | 75 | if (t == null || Mathf.chance(0.2)) return; 76 | 77 | if (t.block() == Blocks.air && t.floor().hasSurface()) { 78 | t.setBlock(st.block, Team.sharded, st.rotation); 79 | 80 | if (st.config != null) { 81 | t.build.configureAny(st.config); 82 | } 83 | 84 | if (t.build != null) { 85 | t.build.damage(Mathf.random((float) (t.build.health * 0.95))); 86 | } 87 | } 88 | }); 89 | } 90 | 91 | @Override 92 | public String toString() { 93 | return "Ruins"; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/serpulo/Salines.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.serpulo; 2 | 3 | import arc.math.Mathf; 4 | import arc.util.noise.Simplex; 5 | import mindustry.content.Blocks; 6 | import mindustry.maps.filters.OreFilter; 7 | import mindustry.world.Tile; 8 | import sectorized.world.map.Biomes; 9 | import sectorized.world.map.biomes.SerpuloBiome; 10 | import sectorized.world.map.generator.BlockG; 11 | import sectorized.world.map.generator.Generator; 12 | import sectorized.world.map.generator.SimplexGenerator2D; 13 | 14 | public class Salines extends SerpuloBiome { 15 | private final int seed = Mathf.random(99999999); 16 | 17 | public Salines() { 18 | super(new SimplexGenerator2D(new Generator[][]{ 19 | {new SimplexGenerator2D(new Generator[][]{ 20 | {BlockG.tar, BlockG.charr}, 21 | {BlockG.shale, BlockG.tar}, 22 | }, 12, 0.5, 0.1, 1.15, 12, 0.5, 0.1, 1.15), BlockG.charr, BlockG.water, BlockG.water, BlockG.water}, 23 | {BlockG.shale, BlockG.salt, BlockG.salt, BlockG.dacite, BlockG.water}, 24 | {BlockG.stone, BlockG.sand, BlockG.sand, BlockG.salt, BlockG.darksand}, 25 | {BlockG.salt, BlockG.dacite, BlockG.salt, BlockG.darksand, BlockG.darksand}, 26 | {BlockG.slag, BlockG.salt, BlockG.darksand, BlockG.darksand, new SimplexGenerator2D(new Generator[][]{ 27 | {BlockG.slag, BlockG.magmarock}, 28 | {BlockG.magmarock, BlockG.hotrock}, 29 | }, 12, 0.5, 0.1, 1.15, 12, 0.5, 0.1, 1.15)} 30 | }, 12, 0.62, 0.01, 1.3, 12, 0.62, 0.01, 1.3)); 31 | 32 | ores.each(o -> ((OreFilter) o).threshold -= 0.03f); 33 | } 34 | 35 | @Override 36 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 37 | double rotation = ((Simplex.noise2d(seed, 12, 0.4, 0.0005, x, y) - 0.1) * 1.25) * Math.PI * 2; 38 | 39 | int rx = (int) (35 * Math.cos(rotation)); 40 | int ry = (int) (35 * Math.sin(rotation)); 41 | 42 | super.sample(x + rx, y + ry, tile, neighbor, proximity); 43 | 44 | if (tile.block() == Blocks.air) { 45 | if (tile.floor() == Blocks.sand && Mathf.chance(0.01)) tile.setBlock(Blocks.sandBoulder); 46 | if (tile.floor() == Blocks.salt && Mathf.chance(0.001)) tile.setBlock(Blocks.boulder); 47 | if (tile.floor() == Blocks.darksand && Mathf.chance(0.005)) tile.setBlock(Blocks.basaltBoulder); 48 | if (tile.floor() == Blocks.grass && Mathf.chance(0.005)) tile.setBlock(Blocks.pine); 49 | } 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "Salines"; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/serpulo/Savanna.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.serpulo; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.maps.filters.OreFilter; 6 | import mindustry.world.Tile; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.SerpuloBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Savanna extends SerpuloBiome { 14 | public Savanna() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.sand, BlockG.darksand, BlockG.darksand, BlockG.darksand, BlockG.grass}, 17 | {BlockG.sand, BlockG.sand, BlockG.grass, BlockG.darksand, BlockG.stone}, 18 | {BlockG.water, BlockG.sand, BlockG.sand, BlockG.darksand, BlockG.darksand}, 19 | {BlockG.water, BlockG.water, BlockG.sand, BlockG.sand, BlockG.darksand}, 20 | {BlockG.water, BlockG.salt, BlockG.sand, BlockG.sand, BlockG.sand} 21 | }, 12, 0.55, 0.005, 1.2, 12, 0.6, 0.003, 1.2)); 22 | 23 | ores.each(o -> ((OreFilter) o).threshold -= 0.03f); 24 | ores.each(o -> { 25 | OreFilter oreFilter = ((OreFilter) o); 26 | if (oreFilter.ore == Blocks.oreThorium) oreFilter.threshold += 0.01f; 27 | }); 28 | } 29 | 30 | @Override 31 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 32 | super.sample(x, y, tile, neighbor, proximity); 33 | 34 | if (tile.block() == Blocks.air) { 35 | if (tile.floor() == Blocks.sand && Mathf.chance(0.01)) tile.setBlock(Blocks.sandBoulder); 36 | if (tile.floor() == Blocks.darksand && Mathf.chance(0.005)) tile.setBlock(Blocks.basaltBoulder); 37 | if (tile.floor() == Blocks.grass && Mathf.chance(0.01)) tile.setBlock(Blocks.pine); 38 | } 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "Savanna"; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/serpulo/Swamp.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.serpulo; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.maps.filters.OreFilter; 6 | import mindustry.world.Tile; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.SerpuloBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Swamp extends SerpuloBiome { 14 | public Swamp() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.tar, BlockG.shale, BlockG.taintedWater, BlockG.grass, BlockG.grass, BlockG.grass}, 17 | {new SimplexGenerator2D(new Generator[][]{ 18 | {BlockG.tar, BlockG.shale}, 19 | {BlockG.shale, BlockG.tar}, 20 | }, 12, 0.5, 0.1, 1.15, 12, 0.5, 0.1, 1.15), BlockG.shale, BlockG.darksand, BlockG.grass, BlockG.grass, BlockG.grass}, 21 | {BlockG.shale, BlockG.darksand, BlockG.darksand, BlockG.grass, BlockG.grass, BlockG.dirt}, 22 | {BlockG.water, BlockG.darksand, BlockG.darksand, BlockG.moss, BlockG.dirt, BlockG.dirt}, 23 | {BlockG.deepwater, BlockG.water, BlockG.moss, BlockG.sporeMoss, BlockG.mud, BlockG.mud} 24 | }, 12, 0.63, 0.01, 1.2, 12, 0.63, 0.008, 1.2)); 25 | 26 | ores.each(o -> ((OreFilter) o).threshold -= 0.04f); 27 | ores.insert(0, new OreFilter() {{ 28 | ore = Blocks.oreScrap; 29 | scl *= 2f; 30 | threshold = 0.87f; 31 | }}); 32 | } 33 | 34 | @Override 35 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 36 | super.sample(x, y, tile, neighbor, proximity); 37 | 38 | if (tile.block() == Blocks.air) { 39 | if (tile.floor() == Blocks.darksand && Mathf.chance(0.005)) tile.setBlock(Blocks.basaltBoulder); 40 | if (tile.floor() == Blocks.grass && Mathf.chance(0.005)) tile.setBlock(Blocks.pine); 41 | if (tile.floor() == Blocks.shale && Mathf.chance(0.003)) tile.setBlock(Blocks.shaleBoulder); 42 | if (tile.floor() == Blocks.moss && Mathf.chance(0.009)) tile.setBlock(Blocks.sporePine); 43 | if (tile.floor() == Blocks.sporeMoss && Mathf.chance(0.01)) tile.setBlock(Blocks.sporeCluster); 44 | } 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "Swamp"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/serpulo/Tundra.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.serpulo; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.maps.filters.OreFilter; 6 | import mindustry.world.Tile; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.SerpuloBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Tundra extends SerpuloBiome { 14 | public Tundra() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.sand, BlockG.sand, BlockG.sand, BlockG.sand, BlockG.snow}, 17 | {BlockG.sand, BlockG.sand, BlockG.snow, BlockG.iceSnow, new SimplexGenerator2D(new Generator[][]{ 18 | {BlockG.tar, BlockG.ice}, 19 | {BlockG.ice, BlockG.cryofluid}, 20 | }, 12, 0.5, 0.1, 1.15, 12, 0.5, 0.1, 1.15)}, 21 | {BlockG.sand, BlockG.snow, BlockG.iceSnow, BlockG.ice, BlockG.ice}, 22 | {BlockG.iceSnow, BlockG.iceSnow, BlockG.ice, BlockG.ice, BlockG.water}, 23 | {BlockG.tar, BlockG.ice, BlockG.sand, BlockG.ice, BlockG.water} 24 | }, 12, 0.55, 0.05, 1.2, 12, 0.6, 0.03, 1.2)); 25 | 26 | ores.each(o -> ((OreFilter) o).threshold -= 0.03f); 27 | ores.each(o -> { 28 | OreFilter oreFilter = ((OreFilter) o); 29 | if (oreFilter.ore == Blocks.oreCoal) oreFilter.threshold -= 0.01f; 30 | }); 31 | ores.insert(0, new OreFilter() {{ 32 | ore = Blocks.oreScrap; 33 | scl *= 0.8f; 34 | threshold = 0.75f; 35 | }}); 36 | } 37 | 38 | @Override 39 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 40 | super.sample(x, y, tile, neighbor, proximity); 41 | 42 | if (tile.block() == Blocks.air) { 43 | if (tile.floor() == Blocks.sand && Mathf.chance(0.01)) tile.setBlock(Blocks.sandBoulder); 44 | if (tile.floor() == Blocks.snow && Mathf.chance(0.003)) tile.setBlock(Blocks.whiteTree); 45 | if (tile.floor() == Blocks.iceSnow && Mathf.chance(0.001)) tile.setBlock(Blocks.whiteTreeDead); 46 | } 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "Tundra"; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/sectorized/world/map/biomes/serpulo/Vulcano.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.biomes.serpulo; 2 | 3 | import arc.math.Mathf; 4 | import mindustry.content.Blocks; 5 | import mindustry.maps.filters.OreFilter; 6 | import mindustry.world.Tile; 7 | import sectorized.world.map.Biomes; 8 | import sectorized.world.map.biomes.SerpuloBiome; 9 | import sectorized.world.map.generator.BlockG; 10 | import sectorized.world.map.generator.Generator; 11 | import sectorized.world.map.generator.SimplexGenerator2D; 12 | 13 | public class Vulcano extends SerpuloBiome { 14 | public Vulcano() { 15 | super(new SimplexGenerator2D(new Generator[][]{ 16 | {BlockG.slag, BlockG.hotrock, BlockG.basalt, BlockG.basalt, BlockG.darksand}, 17 | {BlockG.magmarock, BlockG.basalt, BlockG.basalt, BlockG.darksand, BlockG.darksand}, 18 | {BlockG.hotrock, BlockG.basalt, BlockG.darksand, BlockG.darksand, BlockG.stone}, 19 | {BlockG.basalt, BlockG.darksand, BlockG.darksand, BlockG.stone, BlockG.charr}, 20 | {BlockG.darksand, BlockG.darksand, BlockG.stone, BlockG.craters, new SimplexGenerator2D(new Generator[][]{ 21 | {BlockG.tar, BlockG.dacite}, 22 | {BlockG.dacite, BlockG.craters}, 23 | }, 12, 0.5, 0.01, 1.15, 12, 0.5, 0.01, 1.15)} 24 | }, 12, 0.67, 0.02, 1.3, 12, 0.67, 0.03, 1.3)); 25 | 26 | ores.each(o -> ((OreFilter) o).threshold -= 0.03f); 27 | ores.insert(0, new OreFilter() {{ 28 | ore = Blocks.oreScrap; 29 | scl *= 2f; 30 | threshold = 0.87f; 31 | }}); 32 | } 33 | 34 | @Override 35 | public void sample(int x, int y, Tile tile, Biomes.Biome neighbor, double proximity) { 36 | super.sample(x, y, tile, neighbor, proximity); 37 | 38 | if (tile.block() == Blocks.air) { 39 | if (tile.floor() == Blocks.darksand && Mathf.chance(0.005)) tile.setBlock(Blocks.boulder); 40 | if (tile.floor() == Blocks.darksand && Mathf.chance(0.005)) tile.setBlock(Blocks.basaltBoulder); 41 | if (tile.floor() == Blocks.stone && Mathf.chance(0.01)) tile.setBlock(Blocks.boulder); 42 | if (tile.floor() == Blocks.dacite && Mathf.chance(0.02)) tile.setBlock(Blocks.daciteBoulder); 43 | } 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "Vulcano"; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/sectorized/world/map/generator/BiomeSelection.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.generator; 2 | 3 | import sectorized.world.map.Biomes; 4 | 5 | public class BiomeSelection { 6 | public final Biomes.Biome closest, farthest; 7 | public final double proximity; 8 | 9 | public BiomeSelection(Biomes.Biome closest, Biomes.Biome farthest, double proximity) { 10 | this.closest = closest; 11 | this.farthest = farthest; 12 | this.proximity = proximity; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/sectorized/world/map/generator/BlockG.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.generator; 2 | 3 | import mindustry.content.Blocks; 4 | 5 | public class BlockG { 6 | public static SimpleGenerator placeholder = new SimpleGenerator(null); 7 | 8 | //SERPULO 9 | public static SimpleGenerator sand = new SimpleGenerator(Blocks.sand); 10 | public static SimpleGenerator darksand = new SimpleGenerator(Blocks.darksand); 11 | public static SimpleGenerator slag = new SimpleGenerator(Blocks.slag); 12 | public static SimpleGenerator hotrock = new SimpleGenerator(Blocks.hotrock); 13 | public static SimpleGenerator basalt = new SimpleGenerator(Blocks.basalt); 14 | public static SimpleGenerator magmarock = new SimpleGenerator(Blocks.magmarock); 15 | public static SimpleGenerator stone = new SimpleGenerator(Blocks.stone); 16 | public static SimpleGenerator charr = new SimpleGenerator(Blocks.charr); 17 | public static SimpleGenerator craters = new SimpleGenerator(Blocks.craters); 18 | public static SimpleGenerator dacite = new SimpleGenerator(Blocks.dacite); 19 | public static SimpleGenerator snow = new SimpleGenerator(Blocks.snow); 20 | public static SimpleGenerator iceSnow = new SimpleGenerator(Blocks.iceSnow); 21 | public static SimpleGenerator water = new SimpleGenerator(Blocks.water); 22 | public static SimpleGenerator ice = new SimpleGenerator(Blocks.ice); 23 | public static SimpleGenerator tar = new SimpleGenerator(Blocks.tar); 24 | public static SimpleGenerator shale = new SimpleGenerator(Blocks.shale); 25 | public static SimpleGenerator moss = new SimpleGenerator(Blocks.moss); 26 | public static SimpleGenerator taintedWater = new SimpleGenerator(Blocks.taintedWater); 27 | public static SimpleGenerator grass = new SimpleGenerator(Blocks.grass); 28 | public static SimpleGenerator dirt = new SimpleGenerator(Blocks.dirt); 29 | public static SimpleGenerator mud = new SimpleGenerator(Blocks.mud); 30 | public static SimpleGenerator deepwater = new SimpleGenerator(Blocks.deepwater); 31 | public static SimpleGenerator sporeMoss = new SimpleGenerator(Blocks.sporeMoss); 32 | public static SimpleGenerator salt = new SimpleGenerator(Blocks.salt); 33 | public static SimpleGenerator metalFloorDamaged = new SimpleGenerator(Blocks.metalFloorDamaged); 34 | public static SimpleGenerator metalFloor = new SimpleGenerator(Blocks.metalFloor); 35 | public static SimpleGenerator cryofluid = new SimpleGenerator(Blocks.cryofluid); 36 | 37 | //EREKIR 38 | public static SimpleGenerator crystallineStone = new SimpleGenerator(Blocks.crystallineStone); 39 | public static SimpleGenerator crystalFloor = new SimpleGenerator(Blocks.crystalFloor); 40 | public static SimpleGenerator beryllicStone = new SimpleGenerator(Blocks.beryllicStone); 41 | public static SimpleGenerator rhyolite = new SimpleGenerator(Blocks.rhyolite); 42 | public static SimpleGenerator rhyoliteCrater = new SimpleGenerator(Blocks.rhyoliteCrater); 43 | public static SimpleGenerator roughRhyolite = new SimpleGenerator(Blocks.roughRhyolite); 44 | public static SimpleGenerator regolith = new SimpleGenerator(Blocks.regolith); 45 | public static SimpleGenerator yellowStone = new SimpleGenerator(Blocks.yellowStone); 46 | public static SimpleGenerator yellowStonePlates = new SimpleGenerator(Blocks.yellowStonePlates); 47 | public static SimpleGenerator arkyciteFloor = new SimpleGenerator(Blocks.arkyciteFloor); 48 | public static SimpleGenerator arkyicStone = new SimpleGenerator(Blocks.arkyicStone); 49 | public static SimpleGenerator redStone = new SimpleGenerator(Blocks.redStone); 50 | public static SimpleGenerator redIce = new SimpleGenerator(Blocks.redIce); 51 | public static SimpleGenerator denseRedStone = new SimpleGenerator(Blocks.denseRedStone); 52 | public static SimpleGenerator carbonStone = new SimpleGenerator(Blocks.carbonStone); 53 | } 54 | -------------------------------------------------------------------------------- /src/sectorized/world/map/generator/Generator.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.generator; 2 | 3 | import mindustry.world.Block; 4 | 5 | public abstract class Generator { 6 | public Generator() { 7 | 8 | } 9 | 10 | public Block sample(int x, int y) { 11 | return null; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/sectorized/world/map/generator/SimpleGenerator.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.generator; 2 | 3 | import mindustry.world.Block; 4 | 5 | public class SimpleGenerator extends Generator { 6 | public final Block block; 7 | 8 | public SimpleGenerator(Block block) { 9 | this.block = block; 10 | } 11 | 12 | @Override 13 | public Block sample(int x, int y) { 14 | return this.block; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/sectorized/world/map/generator/SimplexGenerator2D.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.generator; 2 | 3 | import arc.math.Mathf; 4 | import arc.util.noise.Simplex; 5 | import mindustry.world.Block; 6 | 7 | public class SimplexGenerator2D extends Generator { 8 | private final Generator[][] mapping; 9 | 10 | private final int seed1 = Mathf.random(99999999), seed2 = Mathf.random(99999999); 11 | 12 | private final double octaves1, persistence1, scale1, multiplier1; 13 | private final double octaves2, persistence2, scale2, multiplier2; 14 | 15 | public SimplexGenerator2D(Generator[][] mapping, double octaves1, double persistence1, double scale1, double multiplier1, double octaves2, double persistence2, double scale2, double multiplier2) { 16 | this.mapping = mapping; 17 | 18 | this.octaves1 = octaves1; 19 | this.persistence1 = persistence1; 20 | this.scale1 = scale1; 21 | this.multiplier1 = multiplier1; 22 | 23 | this.octaves2 = octaves2; 24 | this.persistence2 = persistence2; 25 | this.scale2 = scale2; 26 | this.multiplier2 = multiplier2; 27 | } 28 | 29 | public SimplexGenerator2D(Generator[][] mapping, double octaves1, double persistence1, double scale1, double octaves2, double persistence2, double scale2) { 30 | this(mapping, octaves1, persistence1, scale1, 1, octaves2, persistence2, scale2, 1); 31 | } 32 | 33 | public Block sample(int x, int y) { 34 | int sampleX = sampleSimplex(seed1, octaves1, persistence1, scale1, multiplier1, x, y, mapping.length - 1); 35 | int sampleY = sampleSimplex(seed2, octaves2, persistence2, scale2, multiplier2, x, y, mapping[0].length - 1); 36 | 37 | return mapping[sampleX][sampleY].sample(x, y); 38 | } 39 | 40 | private int sampleSimplex(int seed, double octaves, double persistence, double scale, double multiplier, int x, int y, int max) { 41 | return (int) Math.max(Math.min((normalizeSimplex(Simplex.noise2d(seed, octaves, persistence, scale, x, y)) * multiplier - (multiplier / 2) + 0.5) * (max + 1), max), 0); 42 | } 43 | 44 | private double normalizeSimplex(double value) { 45 | return (value - 0.1) * 1.25; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/sectorized/world/map/generator/SimplexGenerator3D.java: -------------------------------------------------------------------------------- 1 | package sectorized.world.map.generator; 2 | 3 | import arc.math.Mathf; 4 | import arc.math.geom.Vec3; 5 | import arc.util.noise.Simplex; 6 | import sectorized.world.map.Biomes; 7 | 8 | public class SimplexGenerator3D { 9 | private final Biomes.Biome[][][] mapping; 10 | 11 | private final int seed1 = Mathf.random(99999999), seed2 = Mathf.random(99999999), seed3 = Mathf.random(99999999); 12 | 13 | private final double octaves, persistence, scale, multiplier; 14 | 15 | public SimplexGenerator3D(Biomes.Biome[][][] mapping, double octaves, double persistence, double scale, double multiplier) { 16 | this.mapping = mapping; 17 | 18 | this.octaves = octaves; 19 | this.persistence = persistence; 20 | this.scale = scale; 21 | this.multiplier = multiplier; 22 | } 23 | 24 | public BiomeSelection sample(int x, int y) { 25 | double sampleX = sampleSimplex(seed1, octaves, persistence, scale, multiplier, x, y, mapping.length - 1); 26 | double sampleY = sampleSimplex(seed2, octaves, persistence, scale, multiplier, x, y, mapping[0].length - 1); 27 | double sampleZ = sampleSimplex(seed3, octaves, persistence, scale, multiplier, x, y, mapping[0][0].length - 1); 28 | 29 | Vec3 origin = new Vec3(sampleX, sampleY, sampleZ); 30 | 31 | Vec3[] corners = { 32 | new Vec3((int) sampleX, (int) sampleY, (int) sampleZ), 33 | new Vec3((int) sampleX, (int) sampleY, (int) sampleZ + 1), 34 | new Vec3((int) sampleX, (int) sampleY + 1, (int) sampleZ), 35 | new Vec3((int) sampleX, (int) sampleY + 1, (int) sampleZ + 1), 36 | new Vec3((int) sampleX + 1, (int) sampleY, (int) sampleZ), 37 | new Vec3((int) sampleX + 1, (int) sampleY, (int) sampleZ + 1), 38 | new Vec3((int) sampleX + 1, (int) sampleY + 1, (int) sampleZ), 39 | new Vec3((int) sampleX + 1, (int) sampleY + 1, (int) sampleZ + 1) 40 | }; 41 | 42 | Vec3 c1 = corners[0]; 43 | double cd1 = Double.MAX_VALUE; 44 | for (Vec3 corner : corners) { 45 | double d = origin.dst2(corner); 46 | if (d < cd1) { 47 | c1 = corner; 48 | cd1 = d; 49 | } 50 | } 51 | Vec3 c2 = corners[0]; 52 | double cd2 = Double.MAX_VALUE; 53 | for (Vec3 corner : corners) { 54 | double d = origin.dst2(corner); 55 | if (corner != c1 && d < cd2) { 56 | c2 = corner; 57 | cd2 = d; 58 | } 59 | } 60 | 61 | Biomes.Biome closestBiome = clampedMapping(c1); 62 | Biomes.Biome farthestBiome = clampedMapping(c2); 63 | 64 | return new BiomeSelection(closestBiome, farthestBiome, cd2); 65 | } 66 | 67 | private double sampleSimplex(int seed, double octaves, double persistence, double scale, double multiplier, int x, int y, int max) { 68 | return Math.max(Math.min((normalizeSimplex(Simplex.noise2d(seed, octaves, persistence, scale, x, y)) * multiplier - (multiplier / 2) + 0.5) * (max + 1), max), 0); 69 | } 70 | 71 | private double normalizeSimplex(double value) { 72 | return (value - 0.1) * 1.25; 73 | } 74 | 75 | private Biomes.Biome clampedMapping(Vec3 v) { 76 | int x = Mathf.clamp((int) v.x, 0, mapping.length - 1); 77 | int y = Mathf.clamp((int) v.y, 0, mapping[0].length - 1); 78 | int z = Mathf.clamp((int) v.z, 0, mapping[0][0].length - 1); 79 | 80 | return mapping[x][y][z]; 81 | } 82 | } 83 | --------------------------------------------------------------------------------