diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..e037b8040 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,61 @@ +# A deployment template that works out of the box +# It supports these objectives: +# - Deploy to Maven (Build Job) [Secrets: MAVEN_USER, MAVEN_PASS] +# - Deploy to CurseForge (Upload Job) [Secrets: CURSEFORGE_TOKEN] +# - Deploy to Modrinth (Upload Job) [Secrets: MODRINTH_TOKEN] + +name: Deploy + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Grant Execute Permission for gradlew + run: chmod +x gradlew + + - name: Read gradle.properties + uses: BrycensRanch/read-properties-action@v1 + id: properties + with: + file: gradle.properties + all: true + + - name: Setup Java + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'zulu' + cache: gradle + + - name: Publish to Maven + if: steps.properties.outputs.publish_to_maven == 'true' && steps.properties.outputs.publish_to_local_maven == 'true' + uses: gradle/gradle-build-action@v2 + with: + arguments: | + publish + -P${{ steps.properties.outputs.maven_name }}Username=${{ secrets.MAVEN_USER }} + -P${{ steps.properties.outputs.maven_name }}Password=${{ secrets.MAVEN_PASS }} + + - name: Publish to CurseForge + if: steps.properties.outputs.publish_to_curseforge == 'true' + uses: gradle/gradle-build-action@v2 + env: + CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} + with: + arguments: curseforge + + - name: Publish to Modrinth + if: steps.properties.outputs.publish_to_modrinth == 'true' + uses: gradle/gradle-build-action@v2 + env: + MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} + with: + arguments: modrinth diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..e1adae74f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,318 @@ +Changelog 2.2.5 + +- Commands + - Fixed `addsealant` and `addtorch` + - Fixed `create station` + - Added `d` and `dim` aliases for `/ar goto dimension` + - Added `s` alias for `/ar goto station` + - Added `fd` alias for `/ar fillData` + - Added support for lowercase all subcommands +- OreConfig.xml (oreloader) + - Overall oregen priority unchanged: `planetdefs > oreconfig > config + vanilla/modded` + - Internal oreconfig priority changed: `p+t > p > t > config + vanilla/modded` + - Added "Pressure + Temperature" excact match +- Documentation updated: + - Inside advancedrocketry.cfg + - XML_PLANETDEFS_README.md + - XML_ORECONFIG_README.md + - TEMPLATE_planetdefs.xml + - TEMPLATE_oreconfig.xml + +Changelog 2.2.4 + +- Hovercraft: inverted steering fixed +- FuelRegistry: compatibility issue affecting some cross-mod fluids +- Classtransformer: strengthen EntityPlayer ASM anchor for J21+ environments + +Changelog 2.2.3 + +- Asteroid Dimension: + - fixed corrupt chunks; + - removed black shadows/spots + +- Planetdefs.xml: + - improved save behavior to reduce XML corruption + +- aSync weather (2.1.5) now only applies to native planets (improves compatibility; avoids AR overriding world info/custom dimensions) +- Hovercraft: feels smoother +- Cleaned up commands (and made translatable) +- Updated Chinese localization + +huge thanks to jchung +(and Thermo, ZY, Hades and all other feedback) + +Changelog 2.2.2.1 + +- OreMissions + - Adds support for more modded inventories +- Observatory + - Correctly render items in Asteroids window +- JEI integration + - Machinerecipe: Show Time in Ticks if its less than 1 sec + - Orbital Laser Drill:(only global list for now) + - Asteroids + +- New admin command: + - /advancedrocketry create station [tp] + - Creates a SpaceStation (3x3 cobble) and saves it to orbit + - Optional command: tp + - "/advancedrocketry create station 0 tp" will tp player to a New station orbiting Dim0 + +Changelog 2.2.2 + +- New Blocks + - Orbital Registry + - Scans existing stations/starships/satellites, shows info, prints new chips + - Prevents losing the last chip / reduces need for backups + - Only checks current Dimension (spacestation ->body below) + - Advanced Databus + - Works like DataUnit AND Databus + - Capacity= 2000 * 4 = 8000 (default) + - Keeps data when broken (NOT a "Satellite Component") + +- ItemSatellite + - Added to tooltip: "Data gen: x/s" + +- Rocket + - Added hint: "Press to open GUI" when riding rockets + - Added more error messages for failed launches + - Removed GUI header (fixes fullscreen overlap top left) + - Planet stat bars fixed + +- Warp Controller + - Reduced GC churn + - Removed GUI header (fixes fullscreen overlap top left) + +- Terraforming Terminal + - No Controller = true idle + +- Orbital Laser Drill + - laserDrillPlanet=false: simpler GUI + "void cobble" toggle (big performance boost) + - Early-outs when not constructed / no redstone etc (idle = idle) + +- Station Controllers + - GUI shows if station is anchored + +- Observatory + - Databuses: type could become undefined; now keeps contents on deconstruction + - Server scanning fixed + - Stale lists fixed + +- Area Gravity Controller + - Added explanation for the 6 squares in GUI + +- Rocket Loader/Unloader + Fluid Loader/Unloader + - Accepts most modded tanks/inventories + - Added explanation for the 6 squares in GUI + +- Config + - nuclearRocketsRespectArtifactGating=true + - EnableOrbitalRegistry=true + - dataBusBigMultiplier = 4 + +- Bugfix + - Docking pads blocking rocket dismantle + - Space-to-launch only triggers on "down" press (fixes heavy modpacks) + - Negative/null weather timers crash + - Rare NPE when corrupt / missing starID + - Solar Satellites sending wrong values to receiver + +- Tooltips + - Further polished + +- Translations + - Chinese updated + - English polished + - Many hardcoded English strings fixed + +thanks to (ZY, Hades21_21, Xonazeth, and all reports and feedback) +(RoughlyEnoughIDs 2.2.4 is now compatible with AR again) thanks to jchung + +Changelog 2.2.1-1: + +-Terraforming Terminal: + - GUI: fixed header saying "Satellite Terminal" and polished text + - Hide internal RF Storage since it uses the satellites Power anyway (avoids confusion) +- Other + - Added more tooltips + - Polished tooltips from last update (thanks to Xonazeth!) + +Changelog 2.2.1: + +- AsteroidChip + - Hides 3 unused datatypes from tooltip. + +- AtmosphereDetector + - Fixed GUI-background overlapping hotbar + +- Fuel Station + - Fixed nuclear working fluid filling. + - Smoother energy consumption while fueling. + - JEI integration (respects config per rocket type). + +- ItemSatellite + - Removed false tooltip error; now shows live build preview. + +- WorldServerNotMulti + - Removed super.init() to avoid per-world manager duplication and broken custom data. + +- WirelessTransceiver + - GUI now shows internal buffer. + - Auto-download support. + - Fixed stale states on load. + +- SatelliteTerminal + - Proper, lightweight AutoDownload (With Wireless tranceiver). + - Minor performance tweaks. + - Fixed stale states from last update. + +- Datastorage + - Clears to "Some Random Data" at 0 to avoid locked/stale states. + - Safer vs overriding/voiding types. + +- Observatory + - Each asteroid can only be printed once (no infinite asteroid chips). + - Conditional tooltip explains limit. + - Removed pointless data spending. + +- Pressurized Fluid Tank + - Better tower handling (fluids flow down when stacked). + - Drops and saves correct amount when broken. + +- Station Gravity Controller / Station Altitude Controller + - Performance improvements (less GC, networking, tick spam). + - Only calculates GUI info when open. + - Throttled packets to every 5 ticks. + +- Station Orientation Controller + - Performance improvements as above. + - Smoother rotation and fixed sync issues. + +- Unmanned Vehicle Assembler + - Behaves like Rocket Assembler: + - Rescans rocket stats after build. + - Uses same stat calculation. + - Supports all engine/tank types (compat-guarded). + - Advanced weight (respects config, falls back to block count). + - Rejects invalid rockets with new status messages. + - Updated status syncing. + - Correctly rotates all engines. + +- StationDeployedRocket + - Adopted rocket logic from normal rockets: + - GUI can show 2 fuel bars (biprop). + - Supports all engine/tank types. + +- StorageChunk + - Also checks liquid capacity and gas intake for gas missions. + +- Gas Missions + - New config: + - gasHarvestAmountMultiplier controls per-mission cap (64,000 mB × multiplier). + - gasHarvestInfinite fills all attached tanks up to free space, capped at int max. + - Duration now scales with harvested gas, storage and multipliers (no more multi-hour max runs). + +- GasChargePad + - Hides inherited 0-RF energy capability in Waila/OneProbe. + - Skips scans/lookups if internal tank is empty. + +- RocketMonitor + - Split status/mission into tabs. + - Mission tab shows useful mission details. + - Added Error / status Messages from linked rocket + - Stronger relink on load. + +- Rockets + - Stronger relink on load. + - Failed launch reasons posted to mounted player’s chat. (and linked monitor) + +- Engines + - Nuclear engines auto-stick to nuclear cores. + - Biprop engines stick to tanks (like monoprop). + +- ItemPressureTank + - Stack size increased to 8. + +- MicrowaveReceiver + - Uses same range/lookup logic as Satellite Terminal. + - Fixed NPE. + - Fixed voiding when assembling/disassembling multiblocks. + +- Pump + - Can pump water and lava. + - Now operates every 20 ticks instead of every tick. + - Can be turned off with redstone. + +- Other + - Small cleanups. + - Tooltips added for ~98% of blocks/items. + - JEI: CO2 Scrubber/Oxygen Vent, Fuel Station, Station Assembler. + + +Changelog 2.2.0 + +- JEI Integration + - Satellite Builder: +Satellites + - Satellite Builder: +ChipCopy + +- Guidance Computer Access Hatch + - Fixed render glitch when emitting redstone + +- Satellite Builder + - Rejects invalid items during assembly (soft-fixes crash with invalid core module) + +- Rocket Assembler + - GUI correctly updates error codes/messages to player + - Idle GC craziness SHOULD be fixed; lowered overall GC + +- Station Assembler + - No more "rocket already assembled"; now shows specific failure (e.g., invalid launchpad) + - Correctly updates error codes to client/GUI + - Safer logic; fewer user errors + +- Satellite Terminal + - No more broadcasting UI updates to everyone in 16-block radius + - Send UI data only to actual viewer (less network churn / DDOS-y behavior) + - Downloading data requires power; one-time "Download" button + +- Wireless Transceiver + - Operations throttled to once per 20 ticks; multiple units are phased (don’t run same tick) + - Enable/disable actually turns it off + - GUI shows Network ID so you can verify which plug is connected + - Plugs place on targeted face; top & bottom faces valid + - Extract button toggles insert/extract + - Extract button auto-pulls from satellite to Satellite Terminal internal storage + - NOTE: Still has 100 internal data storage; not voided—stuck in transit if nowhere to go + +- Observatory + - Scrollbar won't reset when selecting an asteroid (may not work with modded container overrides) + - Mousewheel asteroid scrolling + - Process button tooltip explains why it’s not working (when observatory isn’t open) + - Asteroid Chips: + - Improved tooltips/names; choices closer to loot (kept old randomizer logic) + - Fix: chips no longer share same name until “New scan” + +- Rocket Monitor + - Stopped 20x/second polling + - Redstone now event-based (onNeighborChange) + - Fuel/height via rocket entity (delays: fuel 5 ticks, height 3 ticks) + +- Fuel Station + - Stopped all 20x/second behavior + - Early bailout logic to truly idle when idle + - Fix: mono tank could be filled with H2/O2 for 0 burn → infinite free launches + - Safe against overfilling/voiding + +- Rocket Entity + - GUI shows oxidizer bar only if oxidizer tank exists + - On dimension change: preloads 3×3 chunks for 60s from Launch event (reduces desync) + + + + + +solved bugs: +https://github.com/dercodeKoenig/AdvancedRocketry/issues/63 +https://github.com/dercodeKoenig/AdvancedRocketry/issues/62 +https://github.com/dercodeKoenig/AdvancedRocketry/issues/57 +https://github.com/dercodeKoenig/AdvancedRocketry/issues/50 diff --git a/README.md b/README.md index 2a577e6e7..144d86147 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,79 @@ # Advanced Rocketry - Reworked -This is a fork of Advanced Rocketry. -You can download the new mod from curseforge: https://www.curseforge.com/minecraft/mc-mods/advanced-rocketry-2 +A maintained fork of **Advanced Rocketry** for **Minecraft 1.12.2**. -Check out Towards Rocket Science - the modpack all about Advanced Rocketry and Immersive Engineering: https://www.curseforge.com/minecraft/modpacks/towardsrocketscience -(Quests, Tech, Beginner-friendly) +This project continues development of the original mod with ongoing bug fixes, improvements, and quality-of-life updates for modern 1.12.2 modpacks. -original github repo: https://github.com/Advanced-Rocketry/AdvancedRocketry +--- -old documentation: http://arwiki.dmodoomsirius.me/ +## Download -You can see all bugfixes / improvements in the commit history +Download the mod on CurseForge: +**[Advanced Rocketry - Reworked](https://www.curseforge.com/minecraft/mc-mods/advanced-rocketry-2)** - +--- + +## About + +**Advanced Rocketry - Reworked** exists to keep Advanced Rocketry alive and actively maintained for the community. + +The goal of this fork is to improve stability, expand usability for both players and pack developers, and continue refining one of the most ambitious space and progression mods for Minecraft 1.12.2. + +--- + +## Documentation + +### Main Resources + +- **CurseForge:** [Advanced Rocketry - Reworked](https://www.curseforge.com/minecraft/mc-mods/advanced-rocketry-2) +- **Wiki Documentation:** [Advanced Rocketry Wiki](http://arwiki.dmodoomsirius.me/) +- **Change Log:** [`CHANGELOG.md`](./CHANGELOG.md) + + +- **PlanetDefs Documentation**[`XML_PLANETDEFS_README.md`](docs/README_PLANETDEFS.md) +- **OreConfig Documentation**[`XML_ORECONFIG_README.md`](docs/README_ORECONFIG.md) +- **Templates** found `/docs/` + +For pack makers and advanced users, this repository also includes a dedicated reference for configuring `planetDefs.xml`: + +--- + +## Featured Modpacks + +If you want to play Advanced Rocketry as part of a larger progression-focused experience, check out these modpacks: + +### [Towards Rocket Science](https://www.curseforge.com/minecraft/modpacks/towardsrocketscience) + +A modpack built around **Advanced Rocketry** and **Immersive Engineering**. +Great for players who want quests, tech progression, and a more beginner-friendly route into rocket-based gameplay. + +### [MeatballCraft, Dimensional Ascension](https://www.curseforge.com/minecraft/modpacks/meatballcraft) + +A massive expert-style progression pack for players who want deep automation, long-term goals, and a huge endgame. + +### [Enigmatica 2: Expert - Extended](https://www.curseforge.com/minecraft/modpacks/enigmatica-2-expert-extended) + +An extended continuation of the classic expert experience, with heavier progression and plenty of room for Advanced Rocketry to shine. + +--- + +## Development Notes + +Bug fixes, balance changes, and other improvements are tracked in: + +- the repository commit history +- [`CHANGELOG.md`](./CHANGELOG.md) + +--- + +## Credits + +Full credit goes to the original [**Advanced Rocketry**](https://github.com/Advanced-Rocketry/AdvancedRocketry) developers, along with the maintainers of previous forks, for laying the foundation of this project. + +This fork exists to continue development and keep the mod available and useful for the modded Minecraft community. + +--- + +## Support + +If you run into a bug, want to suggest an improvement, or would like to contribute, please use this repository’s issue tracker and pull requests. diff --git a/Template.xml b/Template.xml deleted file mode 100644 index 45b925b6d..000000000 --- a/Template.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - 1,1,1 - 0,0,1 - 100 - 100 - 1000 - 180 - 24000 - 0 - - 1,1,1 - 1,0.5,0.5 - 180 - 100 - 150 - 190 - 100 - 94 - - - - \ No newline at end of file diff --git a/XML_CONFIG_README.txt b/XML_CONFIG_README.txt deleted file mode 100644 index defdf396d..000000000 --- a/XML_CONFIG_README.txt +++ /dev/null @@ -1,311 +0,0 @@ - __..+======|++|;___. - _.:===;======;====|=++++|++__. - _:============;==;=;==+|+|++++|=|+_. - _:====;====;=;=;==;=======++++|+++++++|+_. - _====;=;==;=;==;==;==;=;=;=;=+|+++|+|+|+++++;. - _==;=;====;==;==;==;==;==;==;===+++|=++++++|+|+|;. - .:==;===;=;==;==;==;==;==;==;===;==|+++|++|--- -_ - .===;==;==;=;==;==;==;==;==;==;=;====+|+-- .+; - .======;==;===;==;==;==;==;==;==;==;==-` .+|+; - .==;=;=;==;==;==;==;==;==;==;==;===;:- ____ .+|=++; - .====;==;=;==;==;==;==;==;==;==;==;- +++++: ;|+++|+|; - :==;===;====;==;==;==;==;==;==;=:- -|+|+` _|++++|=+++. -.==;==;=;=;=;==;==;- -- ._+++++|+++|+|; -.==;=;=====;==;=: _+|+|+|++++|+++++ -=======;=;==;==;==... _;++|=+++++|+++++|++: -==;=;=;=;=;==;===;====;.. .:=+|++++|++|+++|+|+++|: -====;==;===;==;=;====;== :====+++|++++|=++|=++++|=: -:==;==;==;==;===;=;: -:. .=====+|+++|+|=|+|=+|++|=+` -.====;==;==;=:----==: :. . .==;==++++|=+++++++|=+|=|+ - ==;==;==;:- --====. :=;= ===;==+|++++|+|++|+++|=++: - .===;==;=:--..====;=;=: .====. .=;====|=+|+++++++++|+++|+ - :=;==;= _. .==;=-= ======;=. .:==;=;+++|=+|+|++|+++++|=` - :==;==== :- = .==;==;==. .:==;==;=|+|=|+++++|=+|+|++` - :===;- .. .= :=====;==;=:=====;=;=|=+++++|++|=+|=+++` - :=.....=: _:=:====;=;==;====;==;====++|+|+++++++|=|+|` - ==============;==;==;==;=;==;==;=|++++++|++|+|=|=; - -========;=;==;==;==;==;==;==;=+++|+|++++|=+++;` - -==;=;==;==;==;==;==;==;==;=+|++++++|++++|;` - -===;==;==;==;==;==;==;=+|=+|+|++++|;- - -=;==;==;==;==;==;==|+++|=+++|:- - --=;==;==;==;==|=+|++;~- - -----------` - - -Welcome to the Advanced Rocketry(AR) advanced configuration readme! - -This document will guide you through manually or semi-manually defining planets for your world! - -To use manual xml planet configuration, download and modify https://github.com/zmaster587/AdvancedRocketry/blob/master/Template.xml and rename to "planetDefs.xml" in the config/advancedRocketry folder - - -Explaination of usable tags: -=============================================================================================================================== - -The "planets" tag should be at the root of the document, this tells AR you are defining your set of planets in the body of this -tag. The "numPlanet" attribute defines how many random planets should be defined in the solar systems, if not specified then -AR will default to six. - -Example usage; generates one random planet around a star named Sol with the temperature of the sun at origin: - - - ... - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "planet" tag surrounds the definition of a planet. If a planet tag is used in the body of another planet tag, the inner -planet tag defines a moon of the outer planet. The planet tag can have the attribute "name". The name attribute specifies the -name of the planet. If the name attribute is not present then the planet is automatically named "Sol-planet_id". - -Example usage; generates one random planet and one planet with manually specified properties named "Earth" with a moon -named "Luna" and another manually specified planet "Mars" - - - - - ... - - ... - - - - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "fogColor" tag specifes the color of the fog on a planet. The body takes three comma seperated values corresponding to -Red, Green, and Blue respectivly. These values can be any decimal number between 0 and 1 inclusive. A 24-bit (6-byte) -Hex color can also be specified by prepending the code with "0x". - -Example usage; specifes a teal color fog using the RGB format. - - - - 0.5,1,1 - ... - - - - -Example usage; specifes the same teal color fog as the previous example using hex format. - - - - 0x7FFFFFF - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "fogColor" tag specifes the color of the sky on a planet. The body takes three comma seperated values corresponding to -Red, Green, and Blue respectivly. These values can be any decimal number between 0 and 1 inclusive. A 24-bit (6-byte) -Hex color can also be specified by prepending the code with "0x". - -Example usage; specifes a teal color sky using the RGB format. - - - - 0.5,1,1 - ... - - - -Example usage; specifes the same teal color sky as the previous example using hex format. - - - - 0x7FFFFFF - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "atmosphereDensity" tag specifes the density of the atmosphere on a planet. Any value greater than 75 is breathable, -100 is Earthlike, anything higher than 100 has a denser atmosphere than Earth and will have thicker fog. Any value less than 75 -is unbreathable and will require a spacesuit and will generate craters. - -Atmosphere density also has an impact on the temerature of the planets, planets with thinner will be colder -and planets with thicker atmospheres will be warmer. - -Max: 200 -Default: 100 -Min: 0 - -Example usage; specifes an atmosphere with the same density as Earth - - - - 100 - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "gravitationalMultiplier" tag specifes the density of the atmosphere on a planet. 100 is earthlike. Any value less than 100 -will result in a gravitational pull less than that of Earth. Any value higher than 110 may result in players being UNABLE to jump -up blocks without assistance from stairs. Values very close to 0 ( < 10) may result in players being unable to fall. -YOU HAVE BEEN WARNED. - -Max: 200 -Default: 100 -Min: 0 -Recommended Max: 110 -Recommended Min: 10 - -Example usage; specifes an atmosphere with the same density as Earth - - - - 100 - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "orbitalDistance" tag specifes the distance of the planet from the body it is orbiting. -For planets orbiting the SUN: - 100 is defined as an earthlike and will result in the sun appearing normal in size. 200 is very far from the sun and will result - in the sun appearing very small. 0 is nearly touching the surface of the host star and will result in the host star taking up a - majority of the sky. - Orbital distance also has an impact on the temerature of the planets, planets far away will be colder and planets closer to the host - star will be warmer. -For MOONS orbiting other planets: - The effects are the same as for planets orbiting a star except the observed host star size is determined by the planet orbiting the sun. - I.E. the apparent size of the sun as seen from the moon is determined by the distance between the Earth and the sun. The apparent - distance of the host planet, however, will be changed by this value. The apparent size of the moon as viewed from the host planet is - also the direct result of this value. - -For planets orbiting the sun, lower values result in higher temperatures. -For moons, this value has no effect on temperatures. - -Max: 200 -Default: 100 -Min: 0 - -Example usage; specifes a distance from the host star to be the same as Earth - - - - 100 - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "orbitalTheta" tag specifes the starting angular displacement relative to the origin in degrees. - -Max: 360 -Default: 0 -Min: 0 - -Example usage; specifes a planet to start exactly opposite the sun from Earth - - - - 180 - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "orbitalPhi" tag specifes the angle of the plane on which the planet rotates around the star or it's host planet, 90 will cause the planet or sun to rise and set in the north and south (the planet would orbit such that it would pass over both poles) whereas 0 with be the normal procession (like orbit over the equator) - -Max: 360 -Default: 0 -Min: 0 - -Example usage; specifes a planet to start exactly opposite the sun from Earth - - - - 180 - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "rotationalPeriod" tag specifes length of a day night cycle for the planet in ticks. Where 20 ticks = 1 second. 24,000/20 = -1,200 seconds = 20 minutes. I strongly recommend not using values < 400 as I found them to be very disorienting and somewhat -motion sickness inducing. - -Max: 2^31 - 1 = 2,147,483,647 (java has no unsigned int...) -Default: 24000 -Min: 1 - -Example usage; specifies a planet named Beebop to have a 10 minute day/night cycle - - - - 12000 - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "biomeIds" tag specifes a comma seperated list of biome ids to generate on the planet. This list can include both vanilla -and modded biome ids. If this tag is not included then the planet will automatically generate a list of biomes from its -atmosphere density, gravitationalMultiplier, and distance from the sun. - -A list of vanilla biomes can be found at http://minecraft.gamepedia.com/Biome -Also can use the command:"/advancedRocketry planet list" to create a dumplist of all biomes from vanilla and installed mods into the instance folder - -Example usage; Planet will generate only ocean and ice plains - - - - 0,12 - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "DIMID" attribute allows a user to specify the exact dimension id that the planet is going to occupy, useful for custom ore gen mods -and more control in general - -Example usage; Planet will generate with the dimid 99 - - - - ... - - - - ------------------------------------------------------------------------------------------------------------------------------- - -The "dimMapping" attribute allows a user to specify that the following planet is a dimension from another mod. Note that it -must be accompanied by a DIMID tag!!! - -Be warned, if another mod does not have a dimension with that ID it will cause a crash if somebody tries to go there! - -Example usage; Adding Twilight forests (with default configs) as a planet around Sol - - - - ... - - - diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..16141a6db --- /dev/null +++ b/build.gradle @@ -0,0 +1,359 @@ +/** + * It is advised that you do not edit anything in the build.gradle; unless you are sure of what you are doing + */ +import com.gtnewhorizons.retrofuturagradle.mcp.InjectTagsTask +import org.jetbrains.changelog.Changelog +import org.jetbrains.gradle.ext.Gradle + +plugins { + id 'java' + id 'java-library' + id 'maven-publish' + id 'org.jetbrains.gradle.plugin.idea-ext' version '1.3' + id 'com.gtnewhorizons.retrofuturagradle' version '2.0.2' + id 'com.matthewprenger.cursegradle' version '1.4.0' apply false + id 'com.modrinth.minotaur' version '2.+' apply false + id 'org.jetbrains.changelog' version '2.5.0' +} + +apply from: 'gradle/scripts/helpers.gradle' + +// Early Assertions +assertProperty 'mod_version' +assertProperty 'root_package' +assertProperty 'mod_id' +assertProperty 'mod_name' + +assertSubProperties 'use_tags', 'tag_class_name' +assertSubProperties 'use_access_transformer', 'access_transformer_locations' +assertSubProperties 'use_mixins', 'mixin_booter_version', 'mixin_refmap' +assertSubProperties 'is_coremod', 'coremod_includes_mod', 'coremod_plugin_class_name' +assertSubProperties 'use_asset_mover', 'asset_mover_version' + +setDefaultProperty 'use_modern_java_syntax', false, false +setDefaultProperty 'generate_sources_jar', true, false +setDefaultProperty 'generate_javadocs_jar', true, false +setDefaultProperty 'mapping_channel', true, 'stable' +setDefaultProperty 'mapping_version', true, '39' +setDefaultProperty 'use_dependency_at_files', true, true +setDefaultProperty 'minecraft_username', true, 'Developer' +setDefaultProperty 'extra_jvm_args', false, '' +setDefaultProperty 'extra_tweak_classes', false, '' +setDefaultProperty 'change_minecraft_sources', false, false + +version = propertyString('mod_version') +group = propertyString('root_package') + +base { + archivesName.set(propertyString('mod_id')) +} + +tasks.decompressDecompiledSources.enabled !propertyBool('change_minecraft_sources') + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(propertyBool('use_modern_java_syntax') ? 16 : 8)) + // Azul covers the most platforms for Java 8 toolchains, crucially including MacOS arm64 + vendor.set(JvmVendorSpec.AZUL) + } + if (propertyBool('generate_sources_jar')) { + withSourcesJar() + } + if (propertyBool('generate_javadocs_jar')) { + withJavadocJar() + } +} + +configurations { + embed + implementation.extendsFrom(embed) +} + +minecraft { + mcVersion.set('1.12.2') + + mcpMappingChannel.set(propertyString('mapping_channel')) + mcpMappingVersion.set(propertyString('mapping_version')) + + useDependencyAccessTransformers.set(propertyBool('use_dependency_at_files')) + + username.set(propertyString('minecraft_username')) + + // Add any additional tweaker classes here + extraTweakClasses.addAll(propertyStringList('extra_tweak_classes')) + + // Add various JVM arguments here for runtime + def args = ['-ea:' + group] + if (propertyBool('use_mixins')) { + args << '-Dmixin.hotSwap=true' + args << '-Dmixin.checks.interfaces=true' + args << '-Dmixin.debug.export=true' + } + extraRunJvmArguments.addAll(args) + extraRunJvmArguments.addAll(propertyStringList('extra_jvm_args')) + + if (propertyBool('use_tags')) { + if (file('tags.properties').exists()) { + Properties props = new Properties().tap { it.load(file('tags.properties').newInputStream()); it } + if (!props.isEmpty()) { + injectedTags.set(props.collectEntries { k, v -> [(k): interpolate(v)] }) + } + } + } +} + +repositories { + maven { + name 'CleanroomMC Maven' + url 'https://maven.cleanroommc.com' + } +} + +dependencies { + if (propertyBool('use_modern_java_syntax')) { + annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:1.0.0' + // Workaround for https://github.com/bsideup/jabel/issues/174 + annotationProcessor 'net.java.dev.jna:jna-platform:5.13.0' + compileOnly ('com.github.bsideup.jabel:jabel-javac-plugin:1.0.0') { + transitive = false + } + // Allow jdk.unsupported classes like sun.misc.Unsafe, workaround for JDK-8206937 and fixes crashes in tests + patchedMinecraft 'me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0' + // Include for tests + testAnnotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:1.0.0' + testCompileOnly('com.github.bsideup.jabel:jabel-javac-plugin:1.0.0') { + transitive = false // We only care about the 1 annotation class + } + } + if (propertyBool('use_asset_mover')) { + implementation "com.cleanroommc:assetmover:${propertyString('asset_mover_version')}" + } + if (propertyBool('use_mixins')) { + String mixin = modUtils.enableMixins("zone.rong:mixinbooter:${propertyString('mixin_booter_version')}", propertyString('mixin_refmap')) + api (mixin) { + transitive = false + } + annotationProcessor 'org.ow2.asm:asm-debug-all:5.2' + annotationProcessor 'com.google.guava:guava:32.1.2-jre' + annotationProcessor 'com.google.code.gson:gson:2.8.9' + annotationProcessor (mixin) { + transitive = false + } + } + if (propertyBool('enable_junit_testing')) { + testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + } +} + +apply from: 'gradle/scripts/dependencies.gradle' + +// Adds Access Transformer files to tasks +if (propertyBool('use_access_transformer')) { + for (def location : propertyStringList('access_transformer_locations')) { + def fileLocation = file("${projectDir}/src/main/resources/${location}") + if (fileLocation.exists()) { + tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(fileLocation) + tasks.srgifyBinpatchedJar.accessTransformerFiles.from(fileLocation) + } else { + throw new GradleException("Access Transformer file [$fileLocation] does not exist!") + } + } +} + +processResources { + + inputs.property 'mod_id', propertyString('mod_id') + inputs.property 'mod_name', propertyString('mod_name') + inputs.property 'mod_version', propertyString('mod_version') + inputs.property 'mod_description', propertyString('mod_description') + inputs.property 'mod_authors', "${propertyStringList('mod_authors', ',').join(', ')}" + inputs.property 'mod_credits', propertyString('mod_credits') + inputs.property 'mod_url', propertyString('mod_url') + inputs.property 'mod_update_json', propertyString('mod_update_json') + inputs.property 'mod_logo_path', propertyString('mod_logo_path') + inputs.property 'mixin_refmap', propertyString('mixin_refmap') + inputs.property 'mixin_package', propertyString('mixin_package') + inputs.property 'mixin_configs', propertyStringList('mixin_configs').join(' ') + + def filterList = ['mcmod.info', 'pack.mcmeta'] + filterList.addAll(propertyStringList('mixin_configs').collect(config -> "mixins.${config}.json" as String)) + + filesMatching(filterList) { fcd -> + fcd.expand( + 'mod_id': propertyString('mod_id'), + 'mod_name': propertyString('mod_name'), + 'mod_version': propertyString('mod_version'), + 'mod_description': propertyString('mod_description'), + 'mod_authors': "${propertyStringList('mod_authors', ',').join(', ')}", + 'mod_credits': propertyString('mod_credits'), + 'mod_url': propertyString('mod_url'), + 'mod_update_json': propertyString('mod_update_json'), + 'mod_logo_path': propertyString('mod_logo_path'), + 'mixin_refmap': propertyString('mixin_refmap'), + 'mixin_package': propertyString('mixin_package') + ) + } + + if (propertyBool('use_access_transformer')) { + rename '(.+_at.cfg)', 'META-INF/$1' + } + +} + +jar { + manifest { + def attribute_map = [:] + if (propertyBool('is_coremod')) { + attribute_map['FMLCorePlugin'] = propertyString('coremod_plugin_class_name') + if (propertyBool('coremod_includes_mod')) { + attribute_map['FMLCorePluginContainsFMLMod'] = true + def currentTasks = gradle.startParameter.taskNames + if (currentTasks[0] == 'build' || currentTasks[0] == 'prepareObfModsFolder' || currentTasks[0] == 'runObfClient') { + attribute_map['ForceLoadAsMod'] = true + } + } + } + if (propertyBool('use_access_transformer')) { + attribute_map['FMLAT'] = propertyString('access_transformer_locations') + } + attributes(attribute_map) + } + // Add all embedded dependencies into the jar + from(provider{ configurations.embed.collect {it.isDirectory() ? it : zipTree(it)} }) +} + +idea { + module { + inheritOutputDirs = true + } + project { + settings { + runConfigurations { + "1. Run Client"(Gradle) { + taskNames = ["runClient"] + } + "2. Run Server"(Gradle) { + taskNames = ["runServer"] + } + "3. Run Obfuscated Client"(Gradle) { + taskNames = ["runObfClient"] + } + "4. Run Obfuscated Server"(Gradle) { + taskNames = ["runObfServer"] + } + } + compiler.javac { + afterEvaluate { + javacAdditionalOptions = "-encoding utf8" + moduleJavacAdditionalOptions = [ + (project.name + ".main"): tasks.compileJava.options.compilerArgs.collect { '"' + it + '"' }.join(' ') + ] + } + } + } + } +} + +compileTestJava { + sourceCompatibility = targetCompatibility = 8 +} + +test { + useJUnitPlatform() + javaLauncher.set(javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(8) + }) + if (propertyBool('show_testing_output')) { + testLogging { + showStandardStreams = true + } + } +} + +String parserChangelog() { + if (!file('CHANGELOG.md').exists()) { + throw new GradleException('publish_with_changelog is true, but CHANGELOG.md does not exist in the workspace!') + } + String parsedChangelog = changelog.renderItem( + changelog.get(propertyString('mod_version')).withHeader(false).withEmptySections(false), + Changelog.OutputType.MARKDOWN) + if (parsedChangelog.isEmpty()) { + throw new GradleException('publish_with_changelog is true, but the changelog for the latest version is empty!') + } + return parsedChangelog +} + +tasks.register('generateMixinJson') { + group 'cleanroom helpers' + def missingConfig = propertyStringList('mixin_configs').findAll(config -> !file("src/main/resources/mixins.${config}.json").exists()) + onlyIf { + if (propertyBool('use_mixins') && propertyBool('generate_mixins_json')) { + return !missingConfig.empty + } + return false + } + doLast { + for (String mixinConfig : missingConfig) { + def file = file("src/main/resources/mixins.${mixinConfig}.json") + file << """{\n\t"package": "",\n\t"required": true,\n\t"refmap": "${propertyString('mixin_refmap')}",\n\t"target": "@env(DEFAULT)",\n\t"minVersion": "0.8.5",\n\t"compatibilityLevel": "JAVA_8",\n\t"mixins": [],\n\t"server": [],\n\t"client": []\n}""" + } + } +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' + if (propertyBool('use_modern_java_syntax')) { + if (it.name in ['compileMcLauncherJava', 'compilePatchedMcJava']) { + return + } + sourceCompatibility = 17 + options.release.set(8) + javaCompiler.set(javaToolchains.compilerFor { + languageVersion.set(JavaLanguageVersion.of(16)) + vendor.set(JvmVendorSpec.AZUL) + }) + } +} + +tasks.register('cleanroomAfterSync') { + group 'cleanroom helpers' + dependsOn 'injectTags', 'generateMixinJson' +} + +if (propertyBool('use_modern_java_syntax')) { + tasks.withType(Javadoc).configureEach { + sourceCompatibility = 17 + } +} + +tasks.named('injectTags', InjectTagsTask).configure { + onlyIf { + return propertyBool('use_tags') && !it.getTags().get().isEmpty() + } + it.outputClassName.set(propertyString('tag_class_name')) +} + +tasks.named('prepareObfModsFolder').configure { + finalizedBy 'prioritizeCoremods' +} + +tasks.register('prioritizeCoremods') { + dependsOn 'prepareObfModsFolder' + doLast { + fileTree('run/obfuscated').forEach { + if (it.isFile() && it.name =~ '(mixinbooter|configanytime)(-)([0-9])+\\.+([0-9])+(.jar)') { + it.renameTo(new File(it.parentFile, "!${it.name}")) + } + } + } +} + +idea.project.settings { + taskTriggers { + afterSync 'cleanroomAfterSync' + } +} + +apply from: 'gradle/scripts/publishing.gradle' +apply from: 'gradle/scripts/extra.gradle' diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 1aa545d72..000000000 --- a/build.gradle.kts +++ /dev/null @@ -1,334 +0,0 @@ -import com.matthewprenger.cursegradle.CurseArtifact -import com.matthewprenger.cursegradle.CurseProject -import com.matthewprenger.cursegradle.CurseRelation -import org.ajoberstar.grgit.Grgit -import org.gradle.internal.jvm.Jvm -import se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask -import java.text.SimpleDateFormat -import java.util.* - -plugins { - idea - id("net.minecraftforge.gradle") version "6.+" - id("wtf.gofancy.fancygradle") version "1.+" - id("org.ajoberstar.grgit") version "4.1.1" - id("com.matthewprenger.cursegradle") version "1.4.0" - id("se.bjurr.gitchangelog.git-changelog-gradle-plugin") version "1.72.0" - `maven-publish` -} - -val mcVersion: String by project -val forgeVersion: String by project -val modVersion: String by project -val archiveBase: String by project - -val libVulpesVersion: String by project -val jeiVersion: String by project -val icVersion: String by project -val gcVersion: String by project - -val startGitRev: String by project - -group = "zmaster587.advancedRocketry" -setProperty("archivesBaseName", archiveBase) - -legacy { - fixClasspath = true -} - -val buildNumber: String by lazy { System.getenv("BUILD_NUMBER") ?: getDate() } - -fun getDate(): String { - return "1" - val format = SimpleDateFormat("HH-mm-dd-MM-yyyy") - format.timeZone = TimeZone.getTimeZone("UTC") - return format.format(Date()) -} - -version = "$modVersion" - -println("$archiveBase v$mcVersion-$version") - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) - } -} - -tasks { - javadoc { - options.encoding = "UTF-8" - } - compileJava { - options.encoding = "UTF-8" - } - compileTestJava { - options.encoding = "UTF-8" - } - -// withType(JavaCompile) { -// options.encoding = "UTF-8" -// } -} - -//configurations.configureEach { -// exclude(group = "net.minecraftforge", module = "mergetool") -//} - -//sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. -tasks.compileJava { - sourceCompatibility = "1.8" - targetCompatibility = "1.8" -} - - -minecraft { - mappings("snapshot", "20171003-1.12") - - accessTransformer(file("src/main/resources/META-INF/accessTransformer.cfg")) - - runs { - create("client") { - properties( - mapOf( - "forge.logging.markers" to "SCAN,REGISTRIES,REGISTRYDUMP,COREMODLOG", - "forge.logging.console.level" to "info" - ) - ) - - workingDirectory = file("run").canonicalPath - - mods { - create("advancedrocketry") { - source(sourceSets["main"]) - } - } - } - create("server") { - properties( - mapOf( - "forge.logging.markers" to "SCAN,REGISTRIES,REGISTRYDUMP,COREMODLOG", - "forge.logging.console.level" to "info"//, "fml.coreMods.load" to "com.gramdatis.core.setup.GramdatisPlugin" - ) - ) - arg("nogui") - - workingDirectory = file("run-server").canonicalPath - - mods { - create("advancedrocketry") { - source(sourceSets["main"]) - } - } - } - } -} - -fancyGradle { - patches { - resources - coremods - codeChickenLib - asm - } -} - -repositories { - mavenCentral() - maven { - name = "mezz.jei" - url = uri("https://dvs1.progwml6.com/files/maven/") - } - maven { - url = uri("https://cursemaven.com") - } - //ivy { - // name = "industrialcraft-2" - // artifactPattern("http://jenkins.ic2.player.to/job/IC2_111/39/artifact/build/libs/[module]-[revision].[ext]") - //} - maven { - // location of a maven mirror for JEI files, as a fallback - name = "ModMaven" - url = uri("https://modmaven.k-4u.nl") - } - //maven { - // name = "Galacticraft" - // url = uri("https://maven.galacticraft.dev/repository/legacy-releases/") - //} -// maven { -// name = "LibVulpes" -// url = uri("http://maven.dmodoomsirius.me/") -// isAllowInsecureProtocol = true -// } - flatDir { - dirs("libs") - } -} - -dependencies { - minecraft(group = "net.minecraftforge", name = "forge", version = "$mcVersion-$forgeVersion") - -// implementation(fg.deobf("curse.maven:industrial-craft-242638:2746892")) - //compileOnly("net.industrial-craft:industrialcraft-2:$icVersion:dev") - //implementation("zmaster587.libVulpes:LibVulpes:$mcVersion-$libVulpesVersion-$libVulpesBuildNum-deobf") - - //compileOnly(fg.deobf("dev.galacticraft:galacticraft-legacy:$gcVersion")) - compileOnly(fg.deobf("curse.maven:galacticraft-legacy-564236:4671122")) - compileOnly(fg.deobf("mezz.jei:jei_${mcVersion}:${jeiVersion}:api")) - implementation(fg.deobf("mezz.jei:jei_${mcVersion}:${jeiVersion}")) // Sorry but it won't start wihout jei... - //runtimeOnly(fg.deobf("mezz.jei:jei_${mcVersion}:${jeiVersion}")) // I think this crashes the game for me when running from IntelliJ - - implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) - compileOnly(fileTree(mapOf("dir" to "libs/compileOnly", "include" to listOf("*.jar")))) - -// implementation ("net.minecraftforge:mergetool:0.2.3.3") - implementation ("net.minecraftforge:mergetool") { version { strictly("0.2.3.3") } } -} - -tasks.processResources { - //includeEmptyDirs = false - inputs.properties( - "advRocketryVersion" to project.version, - "mcVersion" to mcVersion, - "libVulpesVersion" to libVulpesVersion - ) - - filesMatching("mcmod.info") { - expand( - "advRocketryVersion" to project.version, - "mcVersion" to mcVersion, - "libVulpesVersion" to libVulpesVersion - ) - } - - exclude("**/*.sh") -} - -val currentJvm: String = Jvm.current().toString() -println("Current Java version: $currentJvm") - -val gitHash: String by lazy { - val hash: String = if (File(projectDir, ".git").exists()) { - val repo = Grgit.open(mapOf("currentDir" to project.rootDir)) - repo.log().first().abbreviatedId - } else { - "unknown" - } - println("GitHash: $hash") - return@lazy hash -} - -// Name pattern: [archiveBaseName]-[archiveAppendix]-[archiveVersion]-[archiveClassifier].[archiveExtension] -tasks.withType(Jar::class) { - archiveAppendix.set(mcVersion) - manifest { - attributes( - "Built-By" to System.getProperty("user.name"), - "Created-By" to currentJvm, - "Implementation-Title" to archiveBase, - "Implementation-Version" to project.version, - "Git-Hash" to gitHash, - "FMLAT" to "accessTransformer.cfg", - "FMLCorePlugin" to "zmaster587.advancedRocketry.asm.AdvancedRocketryPlugin", - "FMLCorePluginContainsFMLMod" to "true" - ) - } -} - -val deobfJar by tasks.registering(Jar::class) { - from(sourceSets["main"].output) - archiveClassifier.set("deobf") -} - -tasks.build { - dependsOn(deobfJar) -} - -val makeChangelog by tasks.creating(GitChangelogTask::class.java) { - file = file("changelog.html") - untaggedName = "Current release ${mcVersion}-${project.version}" - - //Get the last commit from the cache or config if no cache exists - val lastHashFile = file("lasthash.txt") - - fromCommit = if (!lastHashFile.exists()) - startGitRev - else - lastHashFile.readText() - - lastHashFile.writeText(gitHash) - - toRef = "HEAD" - gitHubIssuePattern = "nonada123"; - templateContent = """ - {{#tags}} -

{{name}}

- - {{/tags}} - """.trimIndent() -} - -curseforge { - apiKey = (project.findProperty("thecursedkey") as String?).orEmpty() - - project(closureOf { - id = "236542" - relations(closureOf { - requiredDependency("libvulpes") - }) - changelog = file("changelog.html") - changelogType = "html" - // Why is it hardcoded to beta tho?.. - releaseType = "release" - addGameVersion(mcVersion) - mainArtifact(tasks.jar.get(), closureOf { - displayName = "AdvancedRocketry ${ project.version } build $buildNumber for $mcVersion" - }) - addArtifact(deobfJar.get(), closureOf { - displayName = "AdvancedRocketry ${ project.version }-deobf build $buildNumber for $mcVersion" - }) - }) -} - -tasks.curseforge { - dependsOn(makeChangelog) -} - -publishing { - repositories { - maven { - url = if (project.findProperty("local") == "true") - uri("$buildDir/build/maven") - else - uri("file:///usr/share/nginx/maven/") - } - } - publications { - register("mavenJava", MavenPublication::class) { - //from(components["java"]) - - artifact(tasks.jar.get()) - artifact(deobfJar.get()) - artifact(makeChangelog.file) - } - } -} - -tasks.curseforge { - dependsOn("reobfJar") -} - -tasks.publish { - dependsOn(makeChangelog) -} - -idea { - module { - inheritOutputDirs = true - } -} diff --git a/docs/README_ORECONFIG.md b/docs/README_ORECONFIG.md new file mode 100644 index 000000000..689b19459 --- /dev/null +++ b/docs/README_ORECONFIG.md @@ -0,0 +1,299 @@ +# Advanced Rocketry `oreConfig.xml` Reference + +This document explains how `oreConfig.xml` is structured and how it behaves. + +Path: + +`config/advRocketry/oreConfig.xml` + + +**Template** found here [`TEMPLATE_oreconfig.xml`](TEMPLATE_oreconfig.xml) + +--- + +## 1. Purpose + +`oreConfig.xml` is a global fallback ore-definition file for AR planets. + +It defines ore-generation presets by: +- pressure class +- temperature class +- exact pressure+temperature combination + +These presets are used only when a planet does **not** define its own per-planet `` in `planetDefs.xml`. + +--- + +## 2. Load Time and Scope + +`oreConfig.xml` is loaded during server startup from: + +`./config/advRocketry/oreConfig.xml` + +If the file is missing, AR creates: + +```xml + + +``` + +This is not a general overworld ore config. It is used for AR planetary world generation through `DimensionProperties.getOreGenProperties(...)` and planet chunk population. + +--- + +## 3. Override Order / Precedence + +Ore behavior priority is: + + planetDefs.xml + > oreConfig.xml + > normal fallback generation + +Meaning: + +1. A per-planet `` in `planetDefs.xml` wins. +2. Otherwise, AR tries `oreConfig.xml`. +3. Otherwise, worldgen falls back normally: + - vanilla ores + - plus AR config ores if `EnableOreGen=true` + +If a planet gets ore properties from either `planetDefs.xml` or `oreConfig.xml`, AR treats that planet as custom-ore-controlled. + +On such planets, `PlanetEventHandler.onWorldGen(...)` denies these `OreGenEvent.GenerateMinable` types: + +- `COAL`- `DIAMOND`- `EMERALD`- `GOLD`- `IRON`- `LAPIS`- `QUARTZ`- `REDSTONE`- `CUSTOM` + +Because AR’s own config ore generator posts `CUSTOM`, AR config ores are also suppressed there. In practice, custom ore properties replace AR normal config ore generation on that planet rather than adding on top. + +Mods using other generation paths may still bypass this. + +--- + +## 4. Basic File Structure + +```xml + + + + + + +``` + +Each `` defines one preset. Each preset contains one or more `` entries. + +--- + +## 5. `` Reference + +### 5.1 Attributes + +#### `pressure` +Pressure-class index. + +Safe values: + +- `0` = `SUPERHIGHPRESSURE` +- `1` = `HIGHPRESSURE` +- `2` = `NORMAL` +- `3` = `LOW` +- `4` = `NONE` + +#### `temp` +Temperature-class index. + +Safe values: + +- `0` = `TOOHOT` +- `1` = `HOT` +- `2` = `NORMAL` +- `3` = `COLD` +- `4` = `FRIGID` +- `5` = `SNOWBALL` + +Use only those safe ranges. The loader clamps against enum `length`, not `length - 1`, so values above the real max can still become invalid later. + +Do **not** use: +- `pressure="5"` +- `temp="6"` + +### 5.2 Matching + +`oreConfig.xml` supports: + +- pressure-only presets +- temp-only presets +- exact pressure+temp presets + +Examples: + + ... + ... + ... + +Selection is based on: + +- pressure class from `originalAtmosphereDensity` +- temperature class from `getAverageTemp()` + +### Internal matching priority inside `oreConfig.xml` + +When more than one entry could match a planet, the effective priority is: + + exact pressure+temp + > pressure-only + > temp-only + > no match = normal fallback generation + +Practical consequence: + +- an exact combined entry like `` overrides both the `pressure="1"` entry and the `temp="4"` entry +- When both a matching pressure-only entry and a matching temp-only entry exist, matching behavior prefers the pressure-only entry. +- if all pressure classes are defined, temp-only entries will usually never be reached +- temp-only entries are most useful when a matching pressure-only entry does not exist + +This matching priority is separate from the higher-level file precedence in **§3**: + + planetDefs.xml + > oreConfig.xml + > normal fallback generation +### 5.3 Notes + +- At least one of `pressure` or `temp` must be present. +- If both are omitted, the entry is skipped. +- Exact pressure+temp mappings work with the fixed loader logic and were verified in fresh-world testing. + +--- + +## 6. `` Reference + +`` entries are read from **attributes**, not child tags. + +Use: + +```xml + +``` + +Do not use: + +```xml + + minecraft:iron_ore + 1 + +``` + +### Attributes + +#### `block` +Required. Block registry name. Invalid names are skipped. + +#### `meta` +Optional. Defaults to `0`. + +#### `minHeight` +Required. Parsed as integer, clamped to at least `1`. + +#### `maxHeight` +Required. Parsed as integer, clamped to `minHeight..255`. + +#### `clumpSize` +Required. Parsed as integer, clamped to `1..255`. + +#### `chancePerChunk` +Required. Parsed as integer, clamped to `1..255`. + +If an `` entry is invalid, it is skipped. If an `` ends up with no valid `` entries, it becomes inactive. + +--- + +## 7. Runtime Generation Behavior + +If a planet uses `oreConfig.xml`, its ore entries are generated during planet chunk population through `CustomizableOreGen`. + +If the planet does not define a custom filler block, AR uses normal `WorldGenMinable(...)`-style stone replacement. + +If the planet does define a custom filler block, AR uses a custom predicate that allows replacement in: +- natural vanilla stone +- the configured filler block’s block type + +That means stone-like filler blocks behave more naturally than non-stone filler blocks such as `minecraft:obsidian`. + +`oreConfig.xml` does not disable biome terrain or surface generation by itself. It only suppresses the denied ore-event path described in **§3**. + +--- + +## 8. Practical Examples + +### Minimal file + +```xml + + +``` + +### Pressure-only preset + +```xml + + + + + + +``` + +### Temperature-only preset + +```xml + + + + + + +``` + +### Exact pressure+temperature preset + +```xml + + + + + + +``` + +--- + +## 9. Recommended Usage + +For predictable behavior: + +1. Use `` as the root. +2. Use only `` children. +3. Use only attribute-based `` entries. +4. Use only safe enum indexes: + - `pressure="0..4"` + - `temp="0..5"` +5. Use exact pressure+temp mappings when you want one specific cell. +6. Use per-planet `` in `planetDefs.xml` for planet-specific behavior. +7. Use `oreConfig.xml` for shared fallback behavior across many planets. + +--- + +## 10. Confirmed Behavior + +Confirmed by code review plus fresh-world testing: + +- per-planet `planetDefs.xml` `` overrides `oreConfig.xml` +- `oreConfig.xml` overrides normal fallback generation on matched planets +- combined pressure+temp mappings work with the fixed loader logic (2.2.5) +- temp-only mappings work +- pressure-only mappings work +- unmatched planets fall back normally +- missing `oreConfig.xml` also falls back normally +- with `EnableOreGen=true`, normal fallback includes AR config ores +- with `EnableOreGen=false`, normal fallback is vanilla-only \ No newline at end of file diff --git a/docs/README_PLANETDEFS.md b/docs/README_PLANETDEFS.md new file mode 100644 index 000000000..baece8179 --- /dev/null +++ b/docs/README_PLANETDEFS.md @@ -0,0 +1,1583 @@ +# Advanced Rocketry `planetDefs.xml` Reference + +This document explains how `planetDefs.xml` is structured and which tags and attributes are supported. + +Place the file at: + +`config/advancedRocketry/planetDefs.xml` + + +**Template** found here [`TEMPLATE_planetdefs.xml`](TEMPLATE_planetdefs.xml) + + +This reference tries to document all fields that are loaded from planetdefs. + +--- + +## 1. Purpose + +`planetDefs.xml` lets you define stars, planets, moons, and planet-specific configuration manually. + +Place the file as: + +`config/advancedRocketry/planetDefs.xml` + +This document is intended as a reference-first replacement for the old XML readme. + +--- + +## 2. Basic File Structure + +### Root structure + +The root element is: + +```xml + +``` + +A galaxy contains one or more `` entries. + +A `` can contain: +- one or more `` entries +- one or more nested `` entries (sub-stars / multi-star systems) + +A `` can contain: +- property tags such as ``, ``, etc. +- nested `` entries, which are treated as moons / child bodies + +### 2.1 Basic examples + +```xml + + + + ... + + + +``` +```xml + + + + ... + + + +``` + +--- + +## 3. Rules and Conventions + +### 3.1 Nesting rules + +- A `` inside a `` defines a planet orbiting that star. +- A `` inside another `` defines a moon / child body. +- A `` inside another `` defines a sub-star. + +### 3.2 Parser behavior + +The loader is tolerant in some places and strict in others. + +Examples: +- Some numeric fields are clamped +- Some invalid values are ignored with warnings +- Some fields use direct `Integer.parseInt(...)` without a `try/catch`; malformed values there may break loading + +### 3.3 Scope of this document + +This document intentionally excludes fields that are only exported/written but not meaningfully loaded from XML. + +Example: +- `avgTemperature` is written by XML export code, but it is not a meaningful author-controlled XML input because temperature is recomputed after load + +--- + +## 4. Star Reference + +### 4.1 `` overview + +Defines a star system entry. + +A top-level `` may contain: +- planets +- sub-stars + +A nested `` is treated as a sub-star. + +### 4.2 `` attributes + +#### `name` +Display name of the star. + +```xml + +``` + +#### `temp` +Star temperature integer. + +```xml + +``` + +Notes: +- Parsed as an integer +- If malformed, the loader falls back to `100` for sub-star parsing + +#### `x` +Galaxy map X position. + +```xml + +``` + +#### `y` +Galaxy map Y position. + +```xml + +``` + +Notes: +- Internally this is used as the star's Z/map Y position + +#### `size` +Star size multiplier. + +```xml + +``` + +Notes: +- Parsed as float + +#### `numPlanets` +Maximum number of randomly generated planets for the star. + +```xml + +``` + +#### `numGasGiants` +Maximum number of randomly generated gas giants for the star. + +```xml + +``` + +Notes: +- These values apply to random planet generation for the star +- Manually defined `` entries can still be added regardless +- For a fully manual system with no extra random planets, use `numPlanets="0"` and `numGasGiants="0"` + +#### `blackHole` +Marks the star as a black hole. + +```xml + +``` + +Accepted values: +- `true` +- `false` + +#### `diskAngle` +Black hole disk angle / star disk angle. + +```xml + +``` + +Notes: +- Parsed as float + +#### `separation` +Only meaningful on nested `` entries. + +```xml + +``` + +Notes: +- Parsed as float +- Used for sub-star separation in multi-star systems + +### 4.3 Star examples + +#### Single star + +```xml + + ... + +``` + +#### Binary star + +```xml + + + ... + +``` + +#### Black hole + +```xml + + ... + +``` + +--- + +## 5 Planet Reference + +### 5.1 `` overview + +Defines a planet or moon. + +- A `` directly inside a `` is a planet. +- A `` inside another `` is a moon / child body. +- A `` could also be defined as `` + - GasGiants: + - Has no surface to land on + - Intended for Gas Collection or cosmetics + +### 5.2 `` attributes + +#### `name` +Planet name. + +```xml + +``` + +#### `DIMID` +Explicit dimension ID. + +```xml + +``` +Note: +- Case sensitive, canonical "DIMID" +#### `dimMapping` +Makes a planet out of a non-native dimension. + +```xml + +``` +The presence of the attribute is what matters. + +Notes: +- This should be paired with a correct `DIMID` +- AR will not enforce weather non-native dimension (2.2.3+) +- As with note above not all entries might apply to other mods dimensions. + +#### 5.3 `customIcon` +Planet icon basename. + +```xml + +``` + + +## Built-in `customIcon` values + +Built-in planet icon basenames: + +`src/main/resources/assets/advancedrocketry/textures/planets/` + +### Standard icons + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ asteroid +
+
+ carbonworld +
+
+ desertworld +
+
+ earthlike +
+
+ gasgiantblue +
+
+ gasgiantbrown +
+
+ gasgiantred +
+
+ iceworld +
+
+ lava +
+
+ marslike +
+
+ moon +
+
+ venusian +
+
+ waterworld +
+ +### Additional normal-only textures + + + + + + + + +
+
+ asteroid_a +
+
+ asteroid_b +
+
+ asteroid_c +
+
+ spoopy +
+ +### Special case + +- `customIcon="void"` is handled specially in the system map and renders the body at size `0`. + +### 5.3.1 Adding your own `customIcon` + +Resource pack should provide: + +```text +assets/advancedrocketry/textures/planets/myplanet.jpg +assets/advancedrocketry/textures/planets/myplanetleo.jpg +``` + +Then reference the basename in `planetDefs.xml`: + +```xml + +``` + +Notes: +- The value is lowercased during lookup +- Custom icons are loaded as `.png` for the normal planet texture and `leo.jpg` for the LEO/orbit texture. +- The LEO texture is used for orbit views +- Built-in examples can be found in the mod resources under: + https://github.com/kaduvill/AdvancedRocketry/tree/1.12/src/main/resources/assets/advancedrocketry/textures/planets + + +--- + +## 6. Planet Property Tags + +### 6.1 Visual and sky settings + +#### `` +Planet fog color. + +Accepted formats: +- comma-separated floats: `r,g,b` +- hex prefixed with `0x` + +Examples: + +```xml +0.5,0.2,1 +or +0x87FFFF +``` + +Notes: +- RGB float components are expected in the range `0` to `1` +- Hex is parsed as an integer after removing the `0x` prefix + +#### `` +Planet sky color. + +Accepted formats: +- comma-separated floats: `r,g,b` +- hex prefixed with `0x` + +Examples: + +```xml +0.3,0.6,1 +or +0x4C99FF +``` + +#### `` +Controls color override behavior for sky/fog rendering. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +Notes: +- Used by world provider sky/fog color calculation + +#### `` +Overrides AR's custom sky renderer for that world. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +Notes: +- This tag only disables AR's custom planet sky for this planet +- Also affected by the global client config option `planetSkyOverride` + - If `planetSkyOverride=false` in the config, AR's custom planet sky is already disabled globally and this tag has no additional effect + +#### `` +Controls planet decoration rendering override. + +```xml +false +``` + +Accepted values: +- `true` +- `false` + +Notes: +- Overrides whether decorators such as shadows / atmosphere-style planet rendering details should be shown + +### 6.2 Atmosphere, gravity, orbit, and rotation + +#### `` +Atmosphere density value. + +```xml +100 +``` + +Meaning: +- `100` is Earthlike +- Values above `75` are generally treated as breathable in the code +- Atmosphere density influences weather, fog, cloud height, and temperature calculations + +Loader clamp: +- Min: `0` +- Max: `1600` + +Notes: +- World provider uses atmosphere density for rain/snow/ice behavior and cloud rendering + +#### `` +Whether the atmosphere contains oxygen. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +Notes: +- Used by atmosphere type logic + +#### `` +Gravity value, using `100 = Earthlike`. + +```xml +100 +``` + +Meaning: +- `100` = `1.0` +- `50` = `0.5` +- `150` = `1.5` + +Loader clamp: +- Min XML value: `0` +- Max XML value: `400` + +Internal conversion: +- Stored as `value / 100f` + +Notes: +- World provider uses this value directly for planetary gravity queries + +#### `` +Distance from the parent body. + +```xml +100 +``` + +Meaning: +- For planets, this is distance from the star +- For moons, this is distance from the parent planet + +Loader clamp: +- Min: `1` +- Max: `2147483647` + +Notes: +- For planets orbiting stars, this affects temperature +- For moons, code uses parent-star distance for solar temperature + +#### `` +Starting angular displacement in degrees. + +```xml +180 +``` + +Notes: +- Parsed as integer degrees +- Converted internally to radians +- The parser stores the value modulo `360` + +#### `` +Orbital plane angle in degrees. + +```xml +90 +``` + +Notes: +- Parsed as integer +- Stored modulo `360` + +#### `` +Whether the body orbits in retrograde. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +#### `` +Length of the day/night cycle in ticks. + +```xml +24000 +``` + +Meaning: +- `24000` ticks = 20 minutes + +Loader rule: +- Must be greater than `0` + +Notes: +- Used by `WorldProviderPlanet.calculateCelestialAngle()` + +#### `` +Sea level value. + +```xml +63 +``` + +Notes: +- Runtime setter clamps to `0..255` + + +#### `` +Controls the `hasRivers` flag. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +Notes: +- This sets `properties.hasRivers` +- The final `hasRivers()` runtime behavior may also depend on atmosphere and temperature if this is not explicitly forced + +### 7.3 Rings and gas giants + +#### `` +Whether the body has rings. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +#### `` +Ring angle integer. + +```xml +70 +``` + +Notes: +- XML loader uses direct `Integer.parseInt(...)` here +- Use a valid integer + +#### `` +Ring color. + +Accepted formats: +- comma-separated floats: `r,g,b` +- hex prefixed with `0x` + +```xml +0.4,0.4,0.7 +``` + +#### `` +Marks the body as a gas giant. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +Notes: +- Intended for use with gas giants and gas missions +- Canonically saved/exported as `GasGiant` + +#### `` +Adds a harvestable gas/fluid name. + +```xml +hydrogen +helium +``` + +Notes: +- The value must resolve through the fluid registry +- Intended for use with gas giants and gas missions + +### 6.4 Biomes + +#### `` +Biome list for the planet. Overrides the automatic biome-selection + +Accepted entry formats: +- numeric biome ID +- biome resource location +- weighted biome entry using `biome;weight` + +Examples: + +```xml +0,12 +minecraft:plains,minecraft:forest +minecraft:plains;30,biomesoplenty:alps;15 +``` + +Notes: +- If a weight is omitted or `0`, default weight is `30` +- Resource locations are preferred over old numeric IDs +- If `` is omitted, the planet falls back to automatic biome selection + - Automatic biome selection is affected by global biome-related config and biome lists, including logic such as blacklist handling and `maxBiomesPerPlanet` +- If `` is provided, the loader uses that explicit biome list instead of automatic biome selection + +#### `` +Per-biome crater frequency list. + +Accepted format: +- `biome;frequency` + +Example: + +```xml +minecraft:desert;100,minecraft:mesa;60 +``` + +Notes: +- The loader expects biome resource locations here +- If frequency is omitted, the loader warns and defaults to `100` + +### 6.5 Generation type and worldgen switches + +#### `` +Generation type integer. + +```xml +1 +``` + +- `0` or omitted: + - normal planet generation +- `1`: + - cave planet generation (based on vanilla nether) + +- `2`: + - Asteroid-belt world + +#### `` +Enable/disable crater generation. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + + +Notes: +- This flag is also gated by the global config option `generateCraters` + - If the global config is `false`, crater generation is disabled globally regardless of this XML value + - If the global config is `true`, this tag can still disable craters for an individual planet +- Actual crater generation also depends on atmospheric conditions + +#### `` +Enable/disable geode generation. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +Notes: +- This flag is also gated by the global config option `generateGeodes` + - If the global config is `false`, geode generation is disabled globally regardless of this XML value + - If the global config is `true`, this tag can still disable geodes for an individual planet + +#### `` +Enable/disable volcano generation. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +Notes: +- Canonical spelling is `generateVolcanos` +- This flag is also gated by the global config option `generateVolcanos` + - If the global config is `false`, volcano generation is disabled globally regardless of this XML value + - If the global config is `true`, this tag can still disable volcanos for an individual planet + +#### `` +Enable/disable structure generation. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +Notes: +- This flag is also gated by the global config option `generateVanillaStructures` + - If the global config is `false`, vanilla/map-feature structures are disabled on all planets regardless of this XML value + - If the global config is `true`, this tag can still disable structures for an individual planet +- Structure generation also requires the planet to be habitable/breathable +#### `` +Enable/disable cave generation. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +#### `` +Crater frequency multiplier. + +```xml +1.5 +``` + +Notes: +- Parsed as float + +#### `` +Volcano frequency multiplier. + +```xml +0.5 +``` + +Notes: +- Parsed as float + +#### `` +Geode frequency multiplier. + +```xml +2.0 +``` + +Notes: +- Tag spelling is exactly `geodefrequencyMultiplier` +- Parsed as float + +### 6.6 Blocks, ores, and loot + +#### `` +Per-planet custom ore generation. + +Example: + +```xml + + + + +``` + +Important: +- The loader reads ore data from `` attributes +- Do not use nested child tags inside `` +- Per-planet `` overrides the fallback ore mapping from `oreConfig.xml` + +Behavior: +- A non-empty per-planet `` gives that planet custom AR ore properties + - `oreConfig.xml` is only used if the planet does not define its own `` +- If a planet has ore properties from either per-planet `` or matching `oreConfig.xml`, AR denies these `OreGenEvent.GenerateMinable` types on that planet: + - `COAL` - `DIAMOND` - `EMERALD` - `GOLD` - `IRON` - `LAPIS` - `QUARTZ` - `REDSTONE` - `CUSTOM` +- Because AR’s own config-driven ore generator (`Copper`, `Tin`, `Rutile`, `Aluminum`, `Iridium`, `Dilithium`) uses `CUSTOM`, those ores are also suppressed on such planets +- In practice, this means per-planet ore properties replace AR’s normal config ore generation on that planet rather than adding to it +- An empty `` does not count; at least one valid `` entry is required for this behavior +- Mods that generate ores through other paths may still bypass this + +Precedence: +- Per-planet `` in `planetDefs.xml` has highest priority +- If `` is absent on that planet, AR falls back to matching entries from `oreConfig.xml` +- If either of those supplies ore properties for the planet, AR’s normal config-driven ore generation is suppressed on that planet +- If neither per-planet `` nor `oreConfig.xml` provides ore properties, AR falls back to its normal global config-driven ore generation +- `` also has a way of disabling normal oregen + + +##### `block` +Block registry name. Required. + +```xml +block="minecraft:iron_ore" +``` + +##### `meta` +Block metadata. Optional. + +```xml +meta="0" +``` + +##### `minHeight` +Minimum generation height. Required. + +```xml +minHeight="1" +``` + +##### `maxHeight` +Maximum generation height. Required. + +```xml +maxHeight="64" +``` + +##### `clumpSize` +Vein size. Required. + +```xml +clumpSize="8" +``` + +##### `chancePerChunk` +Attempts per chunk. Required. + +```xml +chancePerChunk="20" +``` + +Notes: +- Invalid ore entries are skipped with warnings +- `block` must resolve through `Block.getBlockFromName(...)` + +#### `` +Base terrain block override. + +Accepted formats: +- `modid:block` +- `modid:block:meta` + +Examples: + +```xml +minecraft:stone +or +minecraft:stone:3 +``` + +Notes: +- Only one filler block is stored; if multiple are present, the last valid one wins +- If omitted, terrain defaults to `minecraft:stone` +- If set, the planet’s solid terrain mass uses this block instead of stone +- Natural `minecraft:stone` variants preserve more normal biome-style behavior +- Non-stone filler blocks can suppress normal biome/ore generation +- `` does not disable AR custom ore generation from `` + +#### `` +Laser drill ore list. + +Accepted entry formats: +- OreDictionary name, optionally with count +- item registry name, optionally with count and damage + +Examples: + +```xml +oreIron;3,oreGold;1 +or +minecraft:diamond;1;0,minecraft:redstone;8;0 +``` + +Rules: +- Entries are comma-separated +- Each entry uses semicolon-separated parts + +For OreDictionary entries: +- `oreName` +- `oreName;count` + +For item entries: +- `modid:item` +- `modid:item;count` +- `modid:item;count;damage` + +Notes: +- Invalid ore names or item ids are ignored with warnings +- The raw string is preserved internally as `laserDrillOresRaw` +- This is not tested vs JEI-integration + +#### `` +Geode ore whitelist. + +```xml +oreDiamond,oreEmerald +``` + +Notes: +- Comma-separated +- Entries must exist in OreDictionary +- Invalid names are filtered out + +#### `` +Crater ore whitelist. + +```xml +oreIron,oreGold +``` + +Notes: +- Comma-separated +- Entries must exist in OreDictionary +- Invalid names are filtered out + +#### `` +Ocean block override. + +```xml +minecraft:water +``` + +Notes: +- Value is a block resource location +- No metadata is supported here in the XML loader + + +This setting is a full terrain base-material override, not a decorative or secondary filler +#### `` +Required artifact entry. + +Accepted format: +- `item_or_block meta count` + +Examples: + +```xml +minecraft:diamond 0 1 +minecraft:stone 3 16 +``` + +Notes: +- The first token is resolved first as block, then as item +- `meta` defaults to `0` +- `count` defaults to `1` + +### 7.7 Spawn entries + +#### `` +Custom spawn entry. + +Example: + +```xml +minecraft:zombie +``` + +Loader behavior: + +- element text content: + - entity registry name, e.g. `minecraft:zombie` +- supported attributes: + - `weight` + - `groupMin` + - `nbt` + +##### `weight` +Spawn weight. + +```xml +weight="100" +``` + +##### `groupMin` +Minimum group size. + +```xml +groupMin="1" +``` + +##### `nbt` +NBT string passed to the spawn entry. + +```xml +nbt="{CustomName:\"Bob\"}" +``` + +Important parser note: +- The current loader has a bug: + - it reads `groupMin` correctly + - but it also mistakenly reads `groupMax` from the `groupMin` attribute +- As a result, `groupMax` is not actually loaded correctly by the current parser +- For current-code documentation purposes, `groupMax` should not be treated as a reliable working XML input + +Notes: +- If `groupMax` ends up below `groupMin`, it is corrected upward +- Entity lookup first tries registry name, then tries class name +- Invalid NBT can produce fatal configuration errors + +### 7.8 Discovery and progression + +#### `` +Marks the planet as initially known. + +```xml +true +``` + +Accepted values: +- `true` +- `false` + +Notes: +- If true, the planet ID is added to `ARConfiguration.getCurrentConfig().initiallyKnownPlanets` + +### 7.9 Custom weather + +These are used by `WorldProviderPlanet.updateWeather()` when the planet is using custom world info. + +#### `` +Base interval for starting rain. + +```xml +168000 +``` + +#### `` +Base interval for starting thunder. + +```xml +168000 +``` + +#### `` +Extension interval while rain is active. + +```xml +12000 +``` + +#### `` +Extension interval while thunder is active. + +```xml +12000 +``` + +#### `` +Rain mode control. + +```xml +0 +``` + +Meaningful values: +- `-1` = never rain +- `0` = normal cycle +- `1` = always rain + +#### `` +Thunder mode control. + +```xml +0 +``` + +Meaningful values: +- `-1` = never thunder +- `0` = normal cycle +- `1` = always thunder + +Important notes for all weather fields: +- The XML loader uses direct integer parsing here +- Use valid integers +- At runtime, world weather code treats non-positive intervals defensively, but the XML parser itself is not forgiving of malformed values + +--- + +## 7. Value Formats + +### 7.1 Color formats + +Supported by: +- `` +- `` +- `` + +Accepted forms: + +#### RGB floats +```xml +0.5,1,1 +``` + +#### Hex with `0x` +```xml +0x87FFFF +``` + +Notes: +- RGB float input is expected as three comma-separated components +- Hex is parsed after removing `0x` + +### 8.2 Boolean values + +Use: + +```xml +true +false +``` + +Tags using boolean-style values include: +- `` +- `` +- `` +- `` +- `` +- `` +- `` +- `` +- `` +- all `generate...` tags + +### 7.3 Resource-location-like values + +Examples: +- blocks: `minecraft:stone` +- items: `minecraft:diamond` +- biomes: `minecraft:plains` +- entities: `minecraft:zombie` + +Fluids for `` use fluid registry names, such as: +- `hydrogen` +- `oxygen` + +### 7.4 Numeric conventions + +- `100` atmosphere density = Earthlike atmosphere scale +- `100` gravitational multiplier = Earthlike gravity scale +- angles are provided in degrees in XML +- rotational period uses ticks +- sea level uses block Y coordinates + +--- + +## 8. Special Syntax Reference + +### 8.1 `biomeIds` syntax + +Allowed forms: +- `0` +- `minecraft:plains` +- `minecraft:plains;30` + +Combined example: + +```xml +minecraft:plains;30,minecraft:forest;20,12 +``` + +### 8.2 `craterBiomeWeights` syntax + +Allowed form: +- `biome;frequency` + +Example: + +```xml +minecraft:desert;100,minecraft:mesa;60 +``` + +### 8.3 `artifact` syntax + +Format: + +`item_or_block meta count` + +Example: + +```xml +minecraft:diamond 0 1 +``` + +Defaults: +- meta: `0` +- count: `1` + +### 8.4 `fillerBlock` syntax + +Accepted forms: + +```xml +minecraft:stone +or +minecraft:stone:3 +``` + +### 8.5 `spawnable` syntax + +Current reliable format: + +```xml +minecraft:zombie +``` + +With NBT: + +```xml +minecraft:skeleton +``` + +Current parser caveat: +- `groupMax` is not reliably read due to a loader bug + +### 8.6 `oreGen` syntax + +Use attribute-based `` entries: + +```xml + + + +``` + +Do not rely on nested child tags inside `` for loading behavior. + + + +## 9. Practical Examples + +### 9.1 Basic terrestrial planet + +```xml + + 0.7,0.8,1 + 0.4,0.6,1 + 100 + true + 100 + 100 + 0 + 24000 + +``` + +### 9.2 Planet with a moon + +```xml + + 100 + 100 + 100 + 0 + 24000 + + + 0 + false + 16 + 150 + 180 + 24000 + + +``` + +### 9.3 Gas giant with harvestable gases + +```xml + + true + 180 + 220 + 90 + 18000 + hydrogen + +``` + +### 9.4 Binary star system + +```xml + + + + 100 + 100 + 100 + 0 + 24000 + + +``` + +### 9.5 External dimension mapping + +```xml + + 100 + 100 + 140 + 45 + 24000 + +``` + +### 9.6 Planet with custom icon + +```xml + + 120 + 95 + 110 + 270 + 22000 + +``` + +### 9.7 Planet with custom ore generation + +```xml + + 30 + 90 + 80 + 120 + 24000 + + + + + + +``` + +### 9.8 Planet with custom weather + +```xml + + 130 + 100 + 95 + 60 + 24000 + + 6000 + 12000 + 9000 + 6000 + 0 + 0 + +``` + +### 9.9 Planet with custom spawn entries + +```xml + + 80 + 100 + 130 + 180 + 24000 + + minecraft:zombie + minecraft:skeleton + +``` + +--- + +## 10. Common Pitfalls + +### 10.1 `numPlanets`, not `numPlanet` +Attribute name is: + +```xml +numPlanets="..." +``` + +### 10.2 `groupMax` is currently not reliable +Current parser bug: +- `groupMax` is not read correctly +- `groupMin` is mistakenly used for both min and max group size + + +### 10.3 Some author-facing fields from old exports are not real XML inputs +Do not treat exported values such as `avgTemperature` as reliable author-controlled XML settings unless separately confirmed in code. + +--- + +## 11. Fields Intentionally Not Documented Here + +This document intentionally excludes fields that were not confirmed as meaningful current XML inputs. + +Examples: +- fields only written by export code +- fields not meaningfully loaded back +- fields whose behavior was not confirmed when writing this document + +--- + +## 12. Full Example + +```xml + + + + 0.7,0.8,1 + 0.4,0.6,1 + 100 + true + 100 + 100 + 0 + 0 + 24000 + 63 + minecraft:plains;30,minecraft:forest;20 + true + true + true + true + + + 0.9,0.9,0.9 + 0.1,0.1,0.1 + 0 + false + 16 + 150 + 180 + 24000 + true + + + + + true + 180 + 220 + 90 + 18000 + hydrogen + oxygen + true + 70 + 0.6,0.5,0.7 + + + +``` + +--- + +## 13. Resources +App to help build universe. https://github.com/DaIsimsiz/planetDefs-Builder/releases + +) diff --git a/docs/TEMPLATE_oreconfig.xml b/docs/TEMPLATE_oreconfig.xml new file mode 100644 index 000000000..6f4c781d3 --- /dev/null +++ b/docs/TEMPLATE_oreconfig.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/TEMPLATE_planetdefs.xml b/docs/TEMPLATE_planetdefs.xml new file mode 100644 index 000000000..d451f7428 --- /dev/null +++ b/docs/TEMPLATE_planetdefs.xml @@ -0,0 +1,25 @@ + + + + 1,1,1 + 0,0,1 + 100 + 100 + 120 + 180 + 24000 + 0 + + true + 1.0,1.0,1.0 + 1.0,1.0,1.0 + 16 + 45 + 0 + 36000 + 0 + advancedrocketry:moon,advancedrocketry:moondark + + + + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index f80f2cd54..84b5c9845 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,13 +1,130 @@ -org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=false - -# Project -mcVersion=1.12.2 -forgeVersion=14.23.5.2860 -modVersion=2.1.10 -archiveBase=AdvancedRocketry -startGitRev=8e676bd +# Gradle Properties +org.gradle.jvmargs = -Xmx3G + +# Source Options +# Use Modern Java(9+) Syntax (Courtesy of Jabel) +use_modern_java_syntax = false + +# Compilation Options +generate_sources_jar = true +generate_javadocs_jar = false + +# Testing +enable_junit_testing = true +show_testing_output = false + +# Mod Information +# HIGHLY RECOMMEND complying with SemVer for mod_version: https://semver.org/ +mod_version = 2.2.5 +root_package = zmaster587.advancedRocketry +mod_id = advancedrocketry +mod_name = Advanced Rocketry -# Dependencies -libVulpesVersion=0.5.0 +startGitRev=8e676bd jeiVersion=4.16.1.301 + +# Mod Metadata (Optional) +mod_description = A space mod for minecraft, adds planets, rockets, and some machines +mod_url = https://www.curseforge.com/minecraft/mc-mods/advanced-rocketry-2 +mod_update_json = +# Delimit authors with commas +mod_authors = zmaster587 +mod_credits = +mod_logo_path = + +# Mapping Properties +# Using mappings that match LibVulpes +mapping_channel = snapshot +mapping_version = 20171003 +use_dependency_at_files = true + +# Run Configurations +# If multiple arguments/tweak classes are stated, use spaces as the delimiter +minecraft_username = Developer +extra_jvm_args = +extra_tweak_classes = + +# Maven Publishing (Provide secret: MAVEN_USER, MAVEN_PASS) +publish_to_maven = false +# Good for debugging artifacts before uploading to remote maven +# GitHub actions won't run if this is true, test this by running the task `publishToMavenLocal` +publish_to_local_maven = false +maven_name = ${mod_name} +maven_url = + +# Publishing +# release_type can only be: release, beta or alpha (applies to CurseForge / Modrinth) +release_type = beta +publish_with_changelog = ${{ it.file('CHANGELOG.md').exists() }} + +# Publishing to CurseForge (Provide secret: CURSEFORGE_TOKEN) +# To configure dependencies, head to publishing.gradle's curseforge block +publish_to_curseforge = false +# CurseForge project ID must be the numerical ID and not the slug +curseforge_project_id = +curseforge_debug = false + +# Publishing to Modrinth (Provide secret: MODRINTH_TOKEN), the token must have the `CREATE_VERSION` and `PROJECT_WRITE` permissions +# To configure dependencies, head to publishing.gradle's modrinth block +publish_to_modrinth = false +modrinth_project_id = +# Allows gradle to publish updated READMEs to the project body (via the modrinthSyncBody task) +modrinth_sync_readme = false +modrinth_debug = false + +# If any properties changes below this line, refresh gradle again to ensure everything is working correctly. + +# Modify Minecraft Sources +# RetroFuturaGradle allows Minecraft sources to be edited, and have the changes reflected upon running it +# Good for previews when coremodding, or generally seeing how behaviours can change with certain code applied/unapplied +# Turning this on allows Minecraft sources to persist and not regenerate +change_minecraft_sources = false + +# Tags +# A RetroFuturaGradle concept akin to Ant ReplaceTokens +# A class is generated at build-time for compilation, to describe properties that have values that could change at build time such as versioning +# Class name is configurable with the `tag_class_name` property +# Tag properties can be stated in the `tags.properties` file, references are allowed +use_tags = true +tag_class_name = ${root_package}.${mod_id}.Tags + +# Access Transformers +# A way to change visibility of Minecraft's classes, methods and fields +# An example access transformer file is given in the path: `src/main/resources/example_at.cfg` +# AT files should be in the root of src/main/resources with the filename formatted as: `mod_id_at.cfg` +# Use the property `access_transformer_locations` to state custom AT files if you aren't using the default `mod_id_at.cfg` location +# If multiple locations are stated, use spaces as the delimiter +use_access_transformer = true +access_transformer_locations = ${mod_id}_at.cfg + +# Mixins +# Powerful tool to do runtime description changes of classes +# Wiki: https://github.com/SpongePowered/Mixin/wiki + https://github.com/CleanroomMC/MixinBooter/ + https://cleanroommc.com/wiki/forge-mod-development/mixin/preface +# Only use mixins once you understand the underlying structure +use_mixins = false +mixin_booter_version = 10.7 +# A configuration defines a mixin set, and you may have as many mixin sets as you require for your application. +# Each config can only have one and only one package root. +# Generate missing configs, obtain from mixin_configs and generate file base on name convention: "mixins.config_name.json" +# You should change package root once they are generated +generate_mixins_json = false +# Delimit configs with spaces. Should only put configs name instead of full file name +mixin_configs = ${mod_id} +# A refmap is a json that denotes mapping conversions, this json is generated automatically, with the name `mixins.mod_id.refmap.json` +# Use the property `mixin_refmap` if you want it to use a different name, only one name is accepted +mixin_refmap = mixins.${mod_id}.refmap.json + +# Coremods +# The most powerful way to change java classes at runtime, it is however very primitive with little documentation. +# Only make a coremod if you are absolutely sure of what you are doing +# Change the property `coremod_includes_mod` to false if your coremod doesn't have a @Mod annotation +# You MUST state a class name for `coremod_plugin_class_name` if you are making a coremod, the class should implement `IFMLLoadingPlugin` +is_coremod = true +coremod_includes_mod = true +coremod_plugin_class_name = zmaster587.advancedRocketry.asm.AdvancedRocketryPlugin + +# AssetMover +# Convenient way to allow downloading of assets from official vanilla Minecraft servers, CurseForge, or any direct links +# Documentation: https://github.com/CleanroomMC/AssetMover +use_asset_mover = false +asset_mover_version = 2.5 diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle new file mode 100644 index 000000000..743e49696 --- /dev/null +++ b/gradle/scripts/dependencies.gradle @@ -0,0 +1,88 @@ +apply from: 'gradle/scripts/helpers.gradle' + +repositories { + maven { + name 'JEI' + url 'https://dvs1.progwml6.com/files/maven/' + } + maven { + name 'ModMaven' + url 'https://modmaven.dev/' + } + // Other repositories described by default: + // CleanroomMC: https://maven.cleanroommc.com + exclusiveContent { + forRepository { + maven { + name 'CurseMaven' + url 'https://cursemaven.com' + } + } + filter { + includeGroup 'curse.maven' + } + } + exclusiveContent { + forRepository { + maven { + name 'Modrinth' + url 'https://api.modrinth.com/maven' + } + } + filter { + includeGroup 'maven.modrinth' + } + } + mavenLocal() // Must be last for caching to work +} + +dependencies { + // Include StripLatestForgeRequirements by default for the dev env, saves everyone a hassle + runtimeOnly 'com.cleanroommc:strip-latest-forge-requirements:1.0' + // Include OSXNarratorBlocker by default for the dev env, for M1+ Macs + runtimeOnly 'com.cleanroommc:osxnarratorblocker:1.0' + + // Example - Dependency descriptor: + // 'com.google.code.gson:gson:2.8.6' << group: com.google.code.gson, name:gson, version:2.8.6 + // 'group:name:version:classifier' where classifier is optional + + // Example - Deobfuscating dependencies: + // rfg.deobf('curse.maven:had-enough-items-557549:4543375') + // By wrapping a dependency descriptor in rfg.deobf() method call, the dependency is queued for deobfuscation + // When deobfuscating, RFG respects the mapping_channel + mapping_version stated in gradle.properties + + // Example - CurseMaven dependencies: + // 'curse.maven:had-enough-items-557549:4543375' << had-enough-items = project slug, 557549 = project id, 4543375 = file id + // Full documentation: https://cursemaven.com/ + + // Example - Modrinth dependencies: + // 'maven.modrinth:jei:4.16.1.1000' << jei = project name, 4.16.1.1000 = file version + // Full documentation: https://docs.modrinth.com/docs/tutorials/maven/ + + // Common dependency types (configuration): + // implementation = dependency available at both compile time and runtime + // runtimeOnly = runtime dependency + // compileOnly = compile time dependency + // annotationProcessor = annotation processing dependencies + // embed = bundle dependencies into final output artifact (no relocation) + + // Transitive dependencies: + // (Dependencies that your dependency depends on) + // If you wish to exclude transitive dependencies in the described dependencies + // Use a closure as such: + // implementation ('com.google.code.gson:gson:2.8.6') { + // transitive = false + // } + compileOnly 'com.google.code.findbugs:jsr305:3.0.2' + + compileOnly rfg.deobf("mezz.jei:jei_1.12.2:${jeiVersion}:api") + runtimeOnly rfg.deobf("mezz.jei:jei_1.12.2:${jeiVersion}") + + implementation rfg.deobf('curse.maven:lib-vulpes-1038780:5732205') + + compileOnly rfg.deobf("mcjty.theoneprobe:TheOneProbe-1.12:1.12-1.4.28-17") + runtimeOnly rfg.deobf("mcjty.theoneprobe:TheOneProbe-1.12:1.12-1.4.28-17") + + compileOnly rfg.deobf('curse.maven:galacticraft-legacy-564236:4671122') + compileOnly rfg.deobf('curse.maven:matter-overdrive-557428:6439254') +} \ No newline at end of file diff --git a/gradle/scripts/extra.gradle b/gradle/scripts/extra.gradle new file mode 100644 index 000000000..a44cb8e0f --- /dev/null +++ b/gradle/scripts/extra.gradle @@ -0,0 +1,5 @@ +// You may write any gradle buildscript component in this file +// This file is automatically applied after build.gradle + dependencies.gradle is ran + +// If you wish to use the default helper methods, uncomment the line below +// apply from: 'gradle/scripts/helpers.gradle' diff --git a/gradle/scripts/helpers.gradle b/gradle/scripts/helpers.gradle new file mode 100644 index 000000000..0b3f2ee7f --- /dev/null +++ b/gradle/scripts/helpers.gradle @@ -0,0 +1,96 @@ +import groovy.text.SimpleTemplateEngine +import org.codehaus.groovy.runtime.MethodClosure + +ext.propertyString = this.&propertyString as MethodClosure +ext.propertyBool = this.&propertyBool as MethodClosure +ext.propertyStringList = this.&propertyStringList as MethodClosure +ext.interpolate = this.&interpolate as MethodClosure +ext.assertProperty = this.&assertProperty as MethodClosure +ext.assertSubProperties = this.&assertSubProperties as MethodClosure +ext.setDefaultProperty = this.&setDefaultProperty as MethodClosure +ext.assertEnvironmentVariable = this.&assertEnvironmentVariable as MethodClosure + +String propertyString(String key) { + return $property(key).toString() +} + +boolean propertyBool(String key) { + return propertyString(key).toBoolean() +} + +Collection propertyStringList(String key) { + return propertyStringList(key, ' ') +} + +Collection propertyStringList(String key, String delimit) { + return propertyString(key).split(delimit).findAll { !it.isEmpty() } +} + +private Object $property(String key) { + def value = project.findProperty(key) + if (value instanceof String) { + return interpolate(value) + } + return value +} + +String interpolate(String value) { + if (value.startsWith('${{') && value.endsWith('}}')) { + value = value.substring(3, value.length() - 2) + Binding newBinding = new Binding(this.binding.getVariables()) + newBinding.setProperty('it', this) + return new GroovyShell(this.getClass().getClassLoader(), newBinding).evaluate(value) + } + if (value.contains('${')) { + return new SimpleTemplateEngine().createTemplate(value).make(project.properties).toString() + } + return value +} + +void assertProperty(String propertyName) { + def property = property(propertyName) + if (property == null) { + throw new GradleException("Property ${propertyName} is not defined!") + } + if (property.isEmpty()) { + throw new GradleException("Property ${propertyName} is empty!") + } +} + +void assertSubProperties(String propertyName, String... subPropertyNames) { + assertProperty(propertyName) + if (propertyBool(propertyName)) { + for (String subPropertyName : subPropertyNames) { + assertProperty(subPropertyName) + } + } +} + +void setDefaultProperty(String propertyName, boolean warn, defaultValue) { + def property = property(propertyName) + def exists = true + if (property == null) { + exists = false + if (warn) { + project.logger.log(LogLevel.WARN, "Property ${propertyName} is not defined!") + } + } else if (property.isEmpty()) { + exists = false + if (warn) { + project.logger.log(LogLevel.WARN, "Property ${propertyName} is empty!") + } + } + if (!exists) { + project.setProperty(propertyName, defaultValue.toString()) + } +} + +void assertEnvironmentVariable(String propertyName) { + def property = System.getenv(propertyName) + if (property == null) { + throw new GradleException("System Environment Variable $propertyName is not defined!") + } + if (property.isEmpty()) { + throw new GradleException("Property $propertyName is empty!") + } +} diff --git a/gradle/scripts/publishing.gradle b/gradle/scripts/publishing.gradle new file mode 100644 index 000000000..c7897c932 --- /dev/null +++ b/gradle/scripts/publishing.gradle @@ -0,0 +1,107 @@ +apply from: 'gradle/scripts/helpers.gradle' + +setDefaultProperty('publish_to_maven', true, false) +setDefaultProperty('publish_to_curseforge', true, false) +setDefaultProperty('publish_to_modrinth', true, false) + +if (propertyBool('publish_to_maven')) { + assertProperty('maven_name') + assertProperty('maven_url') + publishing { + repositories { + maven { + name propertyString('maven_name').replaceAll("\\s", "") + url propertyString('maven_url') + credentials(PasswordCredentials) + } + } + publications { + mavenJava(MavenPublication) { + from components.java // Publish with standard artifacts + setGroupId(propertyString('root_package'))// Publish with root package as maven group + setArtifactId(propertyString('mod_id')) // Publish artifacts with mod id as the artifact id + + // Custom artifact: + // If you want to publish a different artifact to the one outputted when building normally + // Create a different gradle task (Jar task), in extra.gradle + // Remove the 'from components.java' line above + // Add this line (change the task name): + // artifacts task_name + } + } + } +} + +// Documentation here: https://github.com/matthewprenger/CurseGradle/wiki/ +if (propertyBool('publish_to_curseforge')) { + apply plugin: 'com.matthewprenger.cursegradle' + assertProperty('curseforge_project_id') + assertProperty('release_type') + setDefaultProperty('curseforge_debug', false, false) + curseforge { + apiKey = System.getenv('CURSEFORGE_TOKEN') == null ? "" : System.getenv('CURSEFORGE_TOKEN') + // noinspection GroovyAssignabilityCheck + project { + id = propertyString('curseforge_project_id') + addGameVersion 'Java 8' + addGameVersion 'Forge' + addGameVersion '1.12.2' + releaseType = propertyString('release_type') + if (!propertyBool('publish_with_changelog')) { + changelog = parserChangelog() + changelogType = 'markdown' + } + mainArtifact tasks.reobfJar, { + displayName = "${propertyString('mod_name')} ${propertyString('mod_version')}" + if (propertyBool('use_mixins')) { + relations { + requiredDependency 'mixin-booter' + } + } + if (propertyBool('use_asset_mover')) { + relations { + requiredDependency 'assetmover' + } + } + } + options { + debug = propertyBool('curseforge_debug') + } + } + } +} + +// Documentation here: https://github.com/modrinth/minotaur +if (propertyBool('publish_to_modrinth')) { + apply plugin: 'com.modrinth.minotaur' + assertProperty('modrinth_project_id') + assertProperty('release_type') + setDefaultProperty('modrinth_debug', false, false) + modrinth { + token = System.getenv('MODRINTH_TOKEN') ? "" : System.getenv('MODRINTH_TOKEN') + projectId = propertyString('modrinth_project_id') + versionNumber = propertyString('mod_version') + versionType = propertyString('release_type') + uploadFile = tasks.reobfJar + gameVersions = ['1.12.2'] + loaders = ['forge'] + debugMode = propertyBool('modrinth_debug') + if (propertyBool('use_mixins') || propertyBool('use_asset_mover')) { + dependencies { + if (propertyBool('use_mixins')) { + required.project 'mixinbooter' + } + if (propertyBool('use_asset_mover')) { + required.project 'assetmover' + } + } + } + if (!propertyBool('publish_with_changelog')) { + changelog = parserChangelog() + } + if (propertyBool('modrinth_sync_readme')) { + syncBodyFrom = file('README.md').text + tasks.modrinth.dependsOn(tasks.modrinthSyncBody) + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba771..c1962a79e 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea70190a..cd4b7aa89 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Jul 07 14:46:24 CEST 2024 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 79a61d421..aeb74cbb4 100755 --- a/gradlew +++ b/gradlew @@ -85,9 +85,6 @@ done APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..d3ded3057 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + repositories { + maven { + // RetroFuturaGradle + name 'GTNH Maven' + url 'https://nexus.gtnewhorizons.com/repository/public/' + mavenContent { + includeGroup 'com.gtnewhorizons' + includeGroup 'com.gtnewhorizons.retrofuturagradle' + } + } + gradlePluginPortal() + mavenCentral() + mavenLocal() + } +} + +plugins { + // Automatic toolchain provisioning + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +} + +// Due to an IntelliJ bug, this has to be done +// rootProject.name = archives_base_name +rootProject.name = rootProject.projectDir.getName() \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index 8c3d444bb..000000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - mavenCentral() - maven { - name = "MinecraftForge" - url = uri("https://maven.minecraftforge.net/") - } - maven { - name = "FancyGradle" - url = uri("https://maven.gofancy.wtf/releases") - } - maven { url = uri("https://plugins.gradle.org/m2/") } - maven { - url = uri("https://oss.sonatype.org/content/repositories/snapshots/") - } - } -} - -rootProject.name = "AdvancedRocketry" - -if(file("libVulpes").exists()) { - includeBuild("libVulpes") { - dependencySubstitution { - substitute(module("zmaster587.libVulpes:LibVulpes")).using(project(":")) - } - } -} \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/ARHookLoader.java b/src/main/java/zmaster587/advancedRocketry/ARHookLoader.java deleted file mode 100644 index 80986c48d..000000000 --- a/src/main/java/zmaster587/advancedRocketry/ARHookLoader.java +++ /dev/null @@ -1,18 +0,0 @@ -package zmaster587.advancedRocketry; - - -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.minecraft.HookLoader; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.minecraft.PrimaryClassTransformer; - -public class ARHookLoader extends HookLoader { - - @Override - public String[] getASMTransformerClass() { - return new String[]{PrimaryClassTransformer.class.getName()}; - } - - @Override - public void registerHooks() { - registerHookContainer("zmaster587.advancedRocketry.ARHooks"); - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/ARHooks.java b/src/main/java/zmaster587/advancedRocketry/ARHooks.java deleted file mode 100644 index 9030c4c16..000000000 --- a/src/main/java/zmaster587/advancedRocketry/ARHooks.java +++ /dev/null @@ -1,211 +0,0 @@ -package zmaster587.advancedRocketry; - -import net.minecraft.command.*; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.integrated.IntegratedServer; -import net.minecraft.world.*; -import net.minecraft.world.storage.ISaveHandler; -import net.minecraft.world.storage.WorldInfo; -import net.minecraftforge.common.DimensionManager; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.world.WorldEvent; -import net.minecraftforge.fml.common.FMLLog; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -import zmaster587.advancedRocketry.dimension.DimensionProperties; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.Hook; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.ReturnCondition; -import zmaster587.advancedRocketry.world.WorldServerNotMulti; -import zmaster587.advancedRocketry.world.provider.WorldProviderPlanet; - -import java.util.Random; - -import static net.minecraftforge.common.DimensionManager.getWorld; - -public class ARHooks { - -// @Hook(returnCondition = ReturnCondition.ON_TRUE, booleanReturnConstant = true) -// public static boolean bindEntityTexture(Render instance, Entity entity) { -//// if (Minecraft.getMinecraft().isSingleplayer()) { -//// return false; -//// } -// if (entity instanceof EntityPlayer) { -// ITextureObject t = Skin.forPlayer(entity.getName()).getSkin(); -// if (t == null) { -// return false; -// } -// GlStateManager.bindTexture(t.getGlTextureId()); -// return true; -// } else { -// return false; -// } -// } - - - @Hook(returnCondition = ReturnCondition.ALWAYS) - public static void initDimension(DimensionManager mgr, int dim) { - WorldServer overworld = getWorld(0); - if (overworld == null) { - throw new RuntimeException("Cannot Hotload Dim: Overworld is not Loaded!"); - } - try { - DimensionManager.getProviderType(dim); - } catch (Exception e) { - FMLLog.log.error("Cannot Hotload Dim: {}", dim, e); - return; // If a provider hasn't been registered then we can't hotload the dim - } - MinecraftServer mcServer = overworld.getMinecraftServer(); - ISaveHandler savehandler = overworld.getSaveHandler(); - //WorldSettings worldSettings = new WorldSettings(overworld.getWorldInfo()); - - WorldServer world = (dim == 0 ? overworld : (WorldServer) (new WorldServerNotMulti(mcServer, savehandler, dim, overworld, mcServer.profiler).init())); - world.addEventListener(new ServerWorldEventHandler(mcServer, world)); - MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world)); - if (!mcServer.isSinglePlayer()) { - world.getWorldInfo().setGameType(mcServer.getGameType()); - } - - mcServer.setDifficultyForAllWorlds(mcServer.getDifficulty()); - } - - @Hook(returnCondition = ReturnCondition.ALWAYS) - public static void loadAllWorlds(MinecraftServer server, String saveName, String worldNameIn, long seed, WorldType type, String generatorOptions) { - server.convertMapIfNeeded(saveName); - server.setUserMessage("menu.loadingLevel"); - ISaveHandler isavehandler = server.anvilConverterForAnvilFile.getSaveLoader(saveName, true); - server.setResourcePackFromWorld(server.getFolderName(), isavehandler); - WorldInfo worldinfo = isavehandler.loadWorldInfo(); - WorldSettings worldsettings; - - if (worldinfo == null) { - if (server.isDemo()) { - worldsettings = WorldServerDemo.DEMO_WORLD_SETTINGS; - } else { - worldsettings = new WorldSettings(seed, server.getGameType(), server.canStructuresSpawn(), server.isHardcore(), type); - worldsettings.setGeneratorOptions(generatorOptions); - - if (server.enableBonusChest) { - worldsettings.enableBonusChest(); - } - } - - worldinfo = new WorldInfo(worldsettings, worldNameIn); - } else { - worldinfo.setWorldName(worldNameIn); - worldsettings = new WorldSettings(worldinfo); - } - - WorldServer overWorld = (WorldServer) (server.isDemo() ? new WorldServerDemo(server, isavehandler, worldinfo, 0, server.profiler).init() : new WorldServer(server, isavehandler, worldinfo, 0, server.profiler).init()); - overWorld.initialize(worldsettings); - for (int dim : net.minecraftforge.common.DimensionManager.getStaticDimensionIDs()) { - WorldServer world = (dim == 0 ? overWorld : (WorldServer) new WorldServerNotMulti(server, isavehandler, dim, overWorld, server.profiler).init()); - world.addEventListener(new ServerWorldEventHandler(server, world)); - - if (!server.isSinglePlayer()) { - world.getWorldInfo().setGameType(server.getGameType()); - } - net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.WorldEvent.Load(world)); - } - - server.playerList.setPlayerManager(new WorldServer[]{overWorld}); - server.setDifficultyForAllWorlds(server.getDifficulty()); - server.initialWorldChunkLoad(); - } - - @SideOnly(Side.CLIENT) - @Hook(returnCondition = ReturnCondition.ALWAYS) - public static void loadAllWorlds(IntegratedServer server, String saveName, String worldNameIn, long seed, WorldType type, String generatorOptions) { - server.convertMapIfNeeded(saveName); - ISaveHandler isavehandler = server.getActiveAnvilConverter().getSaveLoader(saveName, true); - server.setResourcePackFromWorld(server.getFolderName(), isavehandler); - WorldInfo worldinfo = isavehandler.loadWorldInfo(); - - if (worldinfo == null) { - worldinfo = new WorldInfo(server.worldSettings, worldNameIn); - } else { - worldinfo.setWorldName(worldNameIn); - } - - WorldServer overWorld = (server.isDemo() ? (WorldServer) (new WorldServerDemo(server, isavehandler, worldinfo, 0, server.profiler)).init() : - (WorldServer) (new WorldServer(server, isavehandler, worldinfo, 0, server.profiler)).init()); - overWorld.initialize(server.worldSettings); - for (int dim : net.minecraftforge.common.DimensionManager.getStaticDimensionIDs()) { - WorldServer world = (dim == 0 ? overWorld : (WorldServer) new WorldServerNotMulti(server, isavehandler, dim, overWorld, server.profiler).init()); - world.addEventListener(new ServerWorldEventHandler(server, world)); - if (!server.isSinglePlayer()) { - world.getWorldInfo().setGameType(server.getGameType()); - } - net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.WorldEvent.Load(world)); - } - - server.getPlayerList().setPlayerManager(new WorldServer[]{overWorld}); - - if (overWorld.getWorldInfo().getDifficulty() == null) { - server.setDifficultyForAllWorlds(server.mc.gameSettings.difficulty); - } - - server.initialWorldChunkLoad(); - } - - @Hook(returnCondition = ReturnCondition.ALWAYS) - public static void execute(CommandWeather command, MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { - if (args.length >= 1 && args.length <= 2) { - int i = (300 + (new Random()).nextInt(600)) * 20; - - if (args.length >= 2) { - i = CommandBase.parseInt(args[1], 1, 1000000) * 20; - } - - World world = sender.getEntityWorld(); - WorldInfo worldinfo = world.getWorldInfo(); - WorldProvider provider = world.provider; - DimensionProperties props = null; - if (provider instanceof WorldProviderPlanet) { - props = ((WorldProviderPlanet) provider).getDimensionProperties(); - } - - if ("clear".equalsIgnoreCase(args[0])) { - if (props != null && (props.getRainMarker() == 1 || props.getThunderMarker() == 1)) { - CommandBase.notifyCommandListener(sender, command, "commands.weather.always_not_clear", new Object[0]); - return; - } - - worldinfo.setCleanWeatherTime(i); - worldinfo.setRainTime(0); - worldinfo.setThunderTime(0); - worldinfo.setRaining(false); - worldinfo.setThundering(false); - CommandBase.notifyCommandListener(sender, command, "commands.weather.clear", new Object[0]); - } else if ("rain".equalsIgnoreCase(args[0])) { - if (props != null && props.getRainMarker() == -1) { - CommandBase.notifyCommandListener(sender, command, "commands.weather.cannot_rain", new Object[0]); - return; - } - - worldinfo.setCleanWeatherTime(0); - worldinfo.setRainTime(i); - worldinfo.setThunderTime(i); - worldinfo.setRaining(true); - worldinfo.setThundering(false); - CommandBase.notifyCommandListener(sender, command, "commands.weather.rain", new Object[0]); - } else { - if (!"thunder".equalsIgnoreCase(args[0])) { - throw new WrongUsageException("commands.weather.usage", new Object[0]); - } - if (props != null && props.getThunderMarker() == -1) { - CommandBase.notifyCommandListener(sender, command, "commands.weather.cannot_thunder", new Object[0]); - return; - } - - worldinfo.setCleanWeatherTime(0); - worldinfo.setRainTime(i); - worldinfo.setThunderTime(i); - worldinfo.setRaining(true); - worldinfo.setThundering(true); - CommandBase.notifyCommandListener(sender, command, "commands.weather.thunder", new Object[0]); - } - } else { - throw new WrongUsageException("commands.weather.usage", new Object[0]); - } - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/AdvancedRocketry.java b/src/main/java/zmaster587/advancedRocketry/AdvancedRocketry.java index 77d37f955..7be2abd38 100644 --- a/src/main/java/zmaster587/advancedRocketry/AdvancedRocketry.java +++ b/src/main/java/zmaster587/advancedRocketry/AdvancedRocketry.java @@ -48,6 +48,7 @@ import net.minecraftforge.oredict.OreDictionary.OreRegisterEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import zmaster587.advancedRocketry.advancedrocketry.Tags; import zmaster587.advancedRocketry.advancements.ARAdvancements; import zmaster587.advancedRocketry.api.*; import zmaster587.advancedRocketry.api.capability.CapabilitySpaceArmor; @@ -58,12 +59,13 @@ import zmaster587.advancedRocketry.block.*; import zmaster587.advancedRocketry.block.inventory.BlockInvHatch; import zmaster587.advancedRocketry.block.multiblock.BlockARHatch; +import zmaster587.advancedRocketry.block.multiblock.BlockDataBusBig; import zmaster587.advancedRocketry.block.plant.BlockLightwoodLeaves; import zmaster587.advancedRocketry.block.plant.BlockLightwoodPlanks; import zmaster587.advancedRocketry.block.plant.BlockLightwoodSapling; import zmaster587.advancedRocketry.block.plant.BlockLightwoodWood; import zmaster587.advancedRocketry.capability.CapabilityProtectiveArmor; -import zmaster587.advancedRocketry.command.WorldCommand; +import zmaster587.advancedRocketry.command.ARCommandRoot; import zmaster587.advancedRocketry.common.CommonProxy; import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.advancedRocketry.dimension.DimensionProperties; @@ -75,8 +77,10 @@ import zmaster587.advancedRocketry.event.EntityEventHandler; import zmaster587.advancedRocketry.event.PlanetEventHandler; import zmaster587.advancedRocketry.event.WorldEvents; +import zmaster587.advancedRocketry.event.WorldInfoHandler; import zmaster587.advancedRocketry.integration.CompatibilityMgr; import zmaster587.advancedRocketry.integration.GalacticCraftHandler; +import zmaster587.advancedRocketry.integration.theoneprobe.TopIntegration; import zmaster587.advancedRocketry.item.*; import zmaster587.advancedRocketry.item.components.ItemJetpack; import zmaster587.advancedRocketry.item.components.ItemPressureTank; @@ -95,6 +99,7 @@ import zmaster587.advancedRocketry.tile.cables.TileLiquidPipe; import zmaster587.advancedRocketry.tile.cables.TileWirelessTransciever; import zmaster587.advancedRocketry.tile.hatch.TileDataBus; +import zmaster587.advancedRocketry.tile.hatch.TileDataBusBig; import zmaster587.advancedRocketry.tile.hatch.TileInvHatch; import zmaster587.advancedRocketry.tile.hatch.TileSatelliteHatch; import zmaster587.advancedRocketry.tile.infrastructure.*; @@ -136,7 +141,6 @@ import zmaster587.libVulpes.tile.energy.TilePlugBase; import zmaster587.libVulpes.tile.multiblock.TileMultiBlock; import zmaster587.libVulpes.tile.multiblock.hatch.TileFluidHatch; -import zmaster587.libVulpes.tile.multiblock.hatch.TileInventoryHatch; import zmaster587.libVulpes.util.FluidUtils; import zmaster587.libVulpes.util.HashedBlockPosition; import zmaster587.libVulpes.util.InputSyncHandler; @@ -151,7 +155,7 @@ import java.util.Map.Entry; -@Mod(modid = "advancedrocketry") +@Mod(modid = Tags.MOD_ID, name = Tags.MOD_NAME, version = Tags.VERSION, dependencies = Constants.DEPENDENCIES) public class AdvancedRocketry { public static final RecipeHandler machineRecipes = new RecipeHandler(); @@ -292,7 +296,8 @@ public void preInit(FMLPreInitializationEvent event) { AdvancedRocketryAPI.atomsphereSealHandler = SealableBlockHandler.INSTANCE; ((SealableBlockHandler) AdvancedRocketryAPI.atomsphereSealHandler).loadDefaultData(); - + // The One Probe integration + TopIntegration.register(); //Configuration --------------------------------------------------------------------------------------------- config = new Configuration(new File(event.getModConfigurationDirectory(), "/" + zmaster587.advancedRocketry.api.ARConfiguration.configFolder + "/advancedRocketry.cfg")); @@ -357,7 +362,7 @@ public void preInit(FMLPreInitializationEvent event) { EntityRegistry.registerModEntity(new ResourceLocation(Constants.modId, "ARPlanetUIButton"), EntityUIButton.class, "ARPlanetUIButton", 6, this, 64, 20, false); EntityRegistry.registerModEntity(new ResourceLocation(Constants.modId, "ARStarUIButton"), EntityUIStar.class, "ARStarUIButton", 7, this, 64, 20, false); EntityRegistry.registerModEntity(new ResourceLocation(Constants.modId, "ARSpaceElevatorCapsule"), EntityElevatorCapsule.class, "ARSpaceElevatorCapsule", 8, this, 64, 20, true); - EntityRegistry.registerModEntity(new ResourceLocation(Constants.modId, "ARHoverCraft"), EntityHoverCraft.class, "hovercraft", 9, this, 64, 3, true); + EntityRegistry.registerModEntity(new ResourceLocation(Constants.modId, "ARHoverCraft"), EntityHoverCraft.class, "hovercraft", 9, this, 64, 1, true); //TileEntity Registration --------------------------------------------------------------------------------------------- GameRegistry.registerTileEntity(TileBrokenPart.class, "ARbrokenPart"); @@ -374,6 +379,7 @@ public void preInit(FMLPreInitializationEvent event) { GameRegistry.registerTileEntity(TileCrystallizer.class, "ARcrystallizer"); GameRegistry.registerTileEntity(TileCuttingMachine.class, "ARcuttingmachine"); GameRegistry.registerTileEntity(TileDataBus.class, "ARdataBus"); + GameRegistry.registerTileEntity(TileDataBusBig.class, "ARdataBusBig"); GameRegistry.registerTileEntity(TileSatelliteHatch.class, "ARsatelliteHatch"); GameRegistry.registerTileEntity(TileInvHatch.class, "ARinventoryHatch"); GameRegistry.registerTileEntity(TileGuidanceComputerAccessHatch.class, "ARguidanceComputerHatch"); @@ -427,6 +433,7 @@ public void preInit(FMLPreInitializationEvent event) { GameRegistry.registerTileEntity(TileCentrifuge.class, new ResourceLocation(Constants.modId, "ARCentrifuge")); GameRegistry.registerTileEntity(TilePrecisionLaserEtcher.class, new ResourceLocation(Constants.modId, "ARPrecisionLaserEtcher")); GameRegistry.registerTileEntity(TileSolarArray.class, new ResourceLocation(Constants.modId, "ARSolarArray")); + GameRegistry.registerTileEntity(TileOrbitalRegistry.class, new ResourceLocation(Constants.modId, "orbitalRegistry")); if (zmaster587.advancedRocketry.api.ARConfiguration.getCurrentConfig().enableGravityController) GameRegistry.registerTileEntity(TileAreaGravityController.class, "ARGravityMachine"); @@ -628,7 +635,7 @@ public void registerBlocks(RegistryEvent.Register evt) { AdvancedRocketryBlocks.blockForceFieldProjector = new BlockForceFieldProjector(Material.IRON).setUnlocalizedName("forceFieldProjector").setCreativeTab(tabAdvRocketry).setHardness(3f); AdvancedRocketryBlocks.blockForceField = new BlockForceField(Material.BARRIER).setBlockUnbreakable().setResistance(6000000.0F).setUnlocalizedName("forceField"); AdvancedRocketryBlocks.blockVacuumLaser = new BlockFullyRotatable(Material.IRON).setUnlocalizedName("vacuumLaser").setCreativeTab(tabAdvRocketry).setHardness(4f); - AdvancedRocketryBlocks.blockPump = new BlockTile(TilePump.class, GuiHandler.guiId.MODULAR.ordinal()).setUnlocalizedName("pump").setCreativeTab(tabAdvRocketry).setHardness(3f); + AdvancedRocketryBlocks.blockPump = new BlockPump(TilePump.class, GuiHandler.guiId.MODULAR.ordinal()).setUnlocalizedName("pump").setCreativeTab(tabAdvRocketry).setHardness(3f); AdvancedRocketryBlocks.blockSuitWorkStation = new BlockSuitWorkstation(TileSuitWorkStation.class, GuiHandler.guiId.MODULAR.ordinal()).setUnlocalizedName("suitWorkStation").setCreativeTab(tabAdvRocketry).setHardness(3f); AdvancedRocketryBlocks.blockPressureTank = new BlockPressurizedFluidTank(Material.IRON).setUnlocalizedName("pressurizedTank").setCreativeTab(tabAdvRocketry).setHardness(3f); AdvancedRocketryBlocks.blockSolarGenerator = new BlockSolarGenerator(TileSolarPanel.class, GuiHandler.guiId.MODULAR.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("solarGenerator"); @@ -649,6 +656,7 @@ public void registerBlocks(RegistryEvent.Register evt) { AdvancedRocketryBlocks.blockPlanetAnalyser = new BlockMultiblockMachine(TileAstrobodyDataProcessor.class, GuiHandler.guiId.MODULARNOINV.ordinal()).setUnlocalizedName("planetanalyser").setCreativeTab(tabAdvRocketry).setHardness(3f); AdvancedRocketryBlocks.blockCentrifuge = new BlockMultiblockMachine(TileCentrifuge.class, GuiHandler.guiId.MODULAR.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("centrifuge"); AdvancedRocketryBlocks.blockSatelliteBuilder = new BlockMultiblockMachine(TileSatelliteBuilder.class, GuiHandler.guiId.MODULAR.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("satelliteBuilder"); + //Energy AdvancedRocketryBlocks.blockBlackHoleGenerator = new BlockMultiblockMachine(TileBlackHoleGenerator.class, GuiHandler.guiId.MODULAR.ordinal()).setUnlocalizedName("blackholegenerator").setCreativeTab(tabAdvRocketry).setHardness(3f); AdvancedRocketryBlocks.blockMicrowaveReciever = new BlockMultiblockMachine(TileMicrowaveReciever.class, GuiHandler.guiId.MODULAR.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("microwaveReciever"); @@ -667,6 +675,11 @@ public void registerBlocks(RegistryEvent.Register evt) { AdvancedRocketryBlocks.blockGravityMachine = new BlockMultiblockMachine(TileAreaGravityController.class, GuiHandler.guiId.MODULARNOINV.ordinal()).setUnlocalizedName("gravityMachine").setCreativeTab(tabAdvRocketry).setHardness(3f); if (ARConfiguration.getCurrentConfig().enableLaserDrill) AdvancedRocketryBlocks.blockSpaceLaser = new BlockOrbitalLaserDrill().setHardness(2f).setCreativeTab(tabAdvRocketry); + if (ARConfiguration.getCurrentConfig().enableOrbitalRegistry) + AdvancedRocketryBlocks.blockOrbitalRegistry = new BlockMultiblockMachine(TileOrbitalRegistry.class, GuiHandler.guiId.MODULARNOINV.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("orbitalRegistry"); + + + //Docking blocks AdvancedRocketryBlocks.blockLaunchpad = new BlockLinkedHorizontalTexture(Material.ROCK).setUnlocalizedName("pad").setCreativeTab(tabAdvRocketry).setHardness(2f).setResistance(10f); AdvancedRocketryBlocks.blockLandingPad = new BlockLandingPad(Material.ROCK).setUnlocalizedName("dockingPad").setHardness(3f).setCreativeTab(tabAdvRocketry); @@ -695,11 +708,14 @@ public void registerBlocks(RegistryEvent.Register evt) { AdvancedRocketryBlocks.blockDeployableRocketBuilder = new BlockTileWithMultitooltip(TileUnmannedVehicleAssembler.class, GuiHandler.guiId.MODULARNOINV.ordinal()).setUnlocalizedName("deployableRocketAssembler").setCreativeTab(tabAdvRocketry).setHardness(3f); //Infrastructure machines AdvancedRocketryBlocks.blockLoader = new BlockARHatch(Material.IRON).setUnlocalizedName("loader").setCreativeTab(tabAdvRocketry).setHardness(3f); + // Big Data Bus Hatch + AdvancedRocketryBlocks.blockDataBusBig = new BlockDataBusBig(Material.IRON).setUnlocalizedName("databusbig").setCreativeTab(tabAdvRocketry).setHardness(3f); AdvancedRocketryBlocks.blockFuelingStation = new BlockTileRedstoneEmitter(TileFuelingStation.class, GuiHandler.guiId.MODULAR.ordinal()).setUnlocalizedName("fuelStation").setCreativeTab(tabAdvRocketry).setHardness(3f); AdvancedRocketryBlocks.blockMonitoringStation = new BlockTileNeighborUpdate(TileRocketMonitoringStation.class, GuiHandler.guiId.MODULARNOINV.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("monitoringstation"); AdvancedRocketryBlocks.blockSatelliteControlCenter = new BlockTile(TileSatelliteTerminal.class, GuiHandler.guiId.MODULAR.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("satelliteMonitor"); AdvancedRocketryBlocks.blockTerraformingTerminal = new BlockTileTerraformer(TileTerraformingTerminal.class, GuiHandler.guiId.MODULAR.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("terraformingTerminal"); AdvancedRocketryBlocks.blockServiceStation = new BlockTile(TileRocketServiceStation.class, GuiHandler.guiId.MODULARNOINV.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("serviceStation"); + //Station machines AdvancedRocketryBlocks.blockWarpShipMonitor = new BlockWarpController(TileWarpController.class, GuiHandler.guiId.MODULARNOINV.ordinal()).setCreativeTab(tabAdvRocketry).setHardness(3f).setUnlocalizedName("stationmonitor"); @@ -823,6 +839,7 @@ public void registerBlocks(RegistryEvent.Register evt) { LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockPlanetAnalyser.setRegistryName("planetAnalyser")); LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockCentrifuge.setRegistryName("centrifuge")); LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockSatelliteBuilder.setRegistryName("satelliteBuilder")); + //Energy LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockBlackHoleGenerator.setRegistryName("blackholegenerator")); LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockMicrowaveReciever.setRegistryName("microwaveReciever")); @@ -840,6 +857,7 @@ public void registerBlocks(RegistryEvent.Register evt) { LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockGravityMachine.setRegistryName("gravityMachine")); if (zmaster587.advancedRocketry.api.ARConfiguration.getCurrentConfig().enableLaserDrill) LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockSpaceLaser.setRegistryName("spaceLaser")); + //Docking blocks LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockLaunchpad.setRegistryName("launchpad")); LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockLandingPad.setRegistryName("landingPad")); @@ -868,10 +886,14 @@ public void registerBlocks(RegistryEvent.Register evt) { LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockDeployableRocketBuilder.setRegistryName("deployableRocketBuilder")); //Infrastructure machines LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockLoader.setRegistryName("loader"), ItemBlockMeta.class, false); + LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockDataBusBig.setRegistryName("databusbig"), zmaster587.advancedRocketry.item.ItemBlockDataBusBig.class, true); LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockServiceStation.setRegistryName("serviceStation")); LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockFuelingStation.setRegistryName("fuelingStation")); LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockMonitoringStation.setRegistryName("monitoringStation")); LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockSatelliteControlCenter.setRegistryName("satelliteControlCenter")); + if (ARConfiguration.getCurrentConfig().enableOrbitalRegistry) + LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockOrbitalRegistry.setRegistryName("orbitalRegistry")); + LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockTerraformingTerminal.setRegistryName("terraformingTerminal")); //Station machines LibVulpesBlocks.registerBlock(AdvancedRocketryBlocks.blockWarpShipMonitor.setRegistryName("warpMonitor")); @@ -1039,6 +1061,7 @@ public void load(FMLInitializationEvent event) { List list = new LinkedList<>(); list.add(new BlockMeta(AdvancedRocketryBlocks.blockLoader, 0)); list.add(new BlockMeta(AdvancedRocketryBlocks.blockLoader, 8)); + list.add(new BlockMeta(AdvancedRocketryBlocks.blockDataBusBig, 0)); TileMultiBlock.addMapping('D', list); machineRecipes.createAutoGennedRecipes(modProducts); @@ -1097,6 +1120,8 @@ public void postInit(FMLPostInitializationEvent event) { // Async weather fix MinecraftForge.EVENT_BUS.register(new EntityEventHandler()); + // Async weather info injection + MinecraftForge.EVENT_BUS.register(new WorldInfoHandler()); CableTickHandler cable = new CableTickHandler(); MinecraftForge.EVENT_BUS.register(cable); @@ -1155,17 +1180,19 @@ public void serverStarted(FMLServerStartedEvent event) { } } + @EventHandler + public void serverAboutToStart(FMLServerAboutToStartEvent event) { + // Populate dimension properties before worlds get loaded + DimensionManager.getInstance().createAndLoadDimensions(resetFromXml); + } + @EventHandler public void serverStarting(FMLServerStartingEvent event) { - event.registerServerCommand(new WorldCommand()); + event.registerServerCommand(new ARCommandRoot()); //Regenerate Chemical Reactor armor recipes TileChemicalReactor.reloadRecipesSpecial(); - - //Open ore files - - //Load Asteroids from XML File file = new File("./config/" + zmaster587.advancedRocketry.api.ARConfiguration.configFolder + "/asteroidConfig.xml"); logger.info("Checking for asteroid config at " + file.getAbsolutePath()); @@ -1254,15 +1281,12 @@ public void serverStarting(FMLServerStartingEvent event) { } } //End open and load ore files - - DimensionManager.getInstance().createAndLoadDimensions(resetFromXml); } @EventHandler public void serverStopped(FMLServerStoppedEvent event) { zmaster587.advancedRocketry.dimension.DimensionManager.getInstance().onServerStopped(); - //zmaster587.advancedRocketry.cable.NetworkRegistry.clearNetworks(); SpaceObjectManager.getSpaceManager().onServerStopped(); zmaster587.advancedRocketry.api.ARConfiguration.getCurrentConfig().MoonId = Constants.INVALID_PLANET; ((BlockSeal) AdvancedRocketryBlocks.blockPipeSealer).clearMap(); @@ -1297,7 +1321,6 @@ public void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) { PacketHandler.sendToPlayer(new PacketSyncKnownPlanets(station.getId(), station.getKnownPlanetList()), player); } } - } } } diff --git a/src/main/java/zmaster587/advancedRocketry/api/ARConfiguration.java b/src/main/java/zmaster587/advancedRocketry/api/ARConfiguration.java index 617d27f1f..8e756b9df 100644 --- a/src/main/java/zmaster587/advancedRocketry/api/ARConfiguration.java +++ b/src/main/java/zmaster587/advancedRocketry/api/ARConfiguration.java @@ -69,6 +69,8 @@ public class ARConfiguration { public double asteroidTBIBurnMult = 1.0; @ConfigProperty(needsSync = true) public double warpTBIBurnMult = 10.0; + @ConfigProperty(needsSync = true) + public int dataBusBigMultiplier = 4; @ConfigProperty public int MoonId = Constants.INVALID_PLANET; @ConfigProperty(needsSync = true) @@ -87,6 +89,8 @@ public class ARConfiguration { public boolean rocketRequireFuel = true; @ConfigProperty public boolean canBeFueledByHand = true; + @ConfigProperty(needsSync = true) + public boolean nuclearRocketsRespectArtifactGating = true; @ConfigProperty public boolean enableNausea = true; @ConfigProperty @@ -153,6 +157,8 @@ public class ARConfiguration { @ConfigProperty public boolean enableLaserDrill; @ConfigProperty + public boolean enableOrbitalRegistry; + @ConfigProperty public int spaceSuitOxygenTime; @ConfigProperty public float suitTankCapacity; @@ -165,6 +171,10 @@ public class ARConfiguration { @ConfigProperty(needsSync = true) public double gasCollectionMult; @ConfigProperty(needsSync = true) + public double gasHarvestAmountMultiplier; + @ConfigProperty(needsSync = true) + public boolean gasHarvestInfinite; + @ConfigProperty(needsSync = true) public double terraformSpeed; @ConfigProperty public boolean terraformRequiresFluid; @@ -358,88 +368,102 @@ public static void loadPreInit() { net.minecraftforge.common.config.Configuration config = arConfig.config; //General - arConfig.allowMakingItemsForOtherMods = config.get(Configuration.CATEGORY_GENERAL, "makeMaterialsForOtherMods", true, "If true the machines from AdvancedRocketry will produce things like plates/rods for other mods even if Advanced Rocketry itself does not use the material (This can increase load time)").getBoolean(); - arConfig.allowSawmillVanillaWood = config.get(Configuration.CATEGORY_GENERAL, "sawMillCutVanillaWood", true, "Should the cutting machine be able to cut vanilla wood into planks").getBoolean(); - arConfig.lowGravityBoots = config.get(Configuration.CATEGORY_GENERAL, "lowGravityBoots", false, "If true the boots only protect the player on planets with low gravity").getBoolean(); + arConfig.allowMakingItemsForOtherMods = config.get(Configuration.CATEGORY_GENERAL, "makeMaterialsForOtherMods", true, "Allow AR machines to make rods/plates e.g for other mods. May increase load time.").getBoolean(); + arConfig.allowSawmillVanillaWood = config.get(Configuration.CATEGORY_GENERAL, "sawMillCutVanillaWood", true, "Allow the cutting machine to turn vanilla wood into planks.").getBoolean(); + arConfig.lowGravityBoots = config.get(Configuration.CATEGORY_GENERAL, "lowGravityBoots", false, "Make low-gravity boots work only on low-gravity planets.").getBoolean(); arConfig.jetPackThrust = (float) config.get(Configuration.CATEGORY_GENERAL, "jetPackForce", 1.3, "Amount of force the jetpack provides with respect to gravity, 1 is the same acceleration as caused by Earth's gravity, 2 is 2x the acceleration caused by Earth's gravity, etc. To make jetpack only work on low gravity planets, simply set it to a value less than 1").getDouble(); - arConfig.buildSpeedMultiplier = (float) config.get(Configuration.CATEGORY_GENERAL, "buildSpeedMultiplier", 1f, "Multiplier for the build speed of the Rocket Builder (0.5 is twice as fast 2 is half as fast").getDouble(); - arConfig.blockTankCapacity = (float) config.get(Configuration.CATEGORY_GENERAL, "blockTankCapacity", 1.0f, "Multiplier for the pressurized tank's (block) capacity", 0, Float.MAX_VALUE).getDouble(); - arConfig.blockEnergyHatchCapacityMultiplier = (float) config.get(Configuration.CATEGORY_GENERAL, "blockEnergyHatchCapacityMultiplier", 1.0f, "Multiplier for the energy hatch capacity", 0, Float.MAX_VALUE).getDouble(); - arConfig.blockLiquidHatchCapacityMultiplier = (float) config.get(Configuration.CATEGORY_GENERAL, "blockLiquidHatchCapacityMultiplier", 1.0f, "Multiplier for the liquid hatch (in/out) capacity", 0, Float.MAX_VALUE).getDouble(); + arConfig.buildSpeedMultiplier = (float) config.get(Configuration.CATEGORY_GENERAL, "buildSpeedMultiplier", 1f, "Multiplier for Rocket Assembler speed. (0.5 is twice as fast 2 is half as fast)").getDouble(); + arConfig.blockTankCapacity = (float) config.get(Configuration.CATEGORY_GENERAL, "blockTankCapacity", 1.0f, "Multiplier for pressurized tank's (block) capacity.", 0, Float.MAX_VALUE).getDouble(); + arConfig.blockEnergyHatchCapacityMultiplier = (float) config.get(Configuration.CATEGORY_GENERAL, "blockEnergyHatchCapacityMultiplier", 1.0f, "Multiplier for energy hatch capacity.", 0, Float.MAX_VALUE).getDouble(); + arConfig.blockLiquidHatchCapacityMultiplier = (float) config.get(Configuration.CATEGORY_GENERAL, "blockLiquidHatchCapacityMultiplier", 1.0f, "Multiplier for liquid hatch capacity.", 0, Float.MAX_VALUE).getDouble(); + arConfig.dataBusBigMultiplier = config.getInt("dataBusBigMultiplier", Configuration.CATEGORY_GENERAL, 4, 1, 20, "Multiplier for the Advanced Data Bus capacity. (Base=2000 -> default= 4 * 2000 = 8000)"); //Enriched Lava in the centrifuge - arConfig.lavaCentrifugeOutputs = config.getStringList("lavaCentrifugeOutputs", Configuration.CATEGORY_GENERAL, new String[]{"nuggetCopper:100", "nuggetIron:100", "nuggetTin:100", "nuggetLead:100", "nuggetSilver:100", "nuggetGold:75", "nuggetDiamond:10", "nuggetUranium:10", "nuggetIridium:1"}, "Outputs and chances of objects from Enriched Lava in the Centrifuge. Format: :. Larger weights are more frequent"); - arConfig.lavaCentrifugePower = config.getInt("lavaCentrifugePower", Configuration.CATEGORY_GENERAL, 10,0,999999,"The power per tick required to process enriched lava"); - arConfig.lavaCentrifugeTime = config.getInt("lavaCentrifugeTime", Configuration.CATEGORY_GENERAL, 50,0,999999,"The time required to process 250mb of enriched lava"); - arConfig.crystalliserMaximumGravity = (float) config.get(Configuration.CATEGORY_GENERAL, "crystalliserMaximumGravity", 0f, "Maximum gravity the crystalliser will function at. Use 0.0 to disable!").getDouble(); - arConfig.enableLaserDrill = config.get(Configuration.CATEGORY_GENERAL, "EnableLaserDrill", true, "Enables the laser drill machine").getBoolean(); - arConfig.spaceLaserPowerMult = (float) config.get(Configuration.CATEGORY_GENERAL, "LaserDrillPowerMultiplier", 1d, "Power multiplier for the laser drill machine").getDouble(); - arConfig.laserDrillPlanet = config.get(Configuration.CATEGORY_GENERAL, "laserDrillPlanet", false, "If true the orbital laser will actually mine blocks on the planet below").getBoolean(); - String[] str = config.getStringList("spaceLaserDimIdBlackList", Configuration.CATEGORY_GENERAL, new String[]{}, "Laser drill will not mine these dimension"); - arConfig.enableTerraforming = config.get(Configuration.CATEGORY_GENERAL, "EnableTerraforming", true, "Enables terraforming items and blocks").getBoolean(); - arConfig.terraformSpeed = config.get(Configuration.CATEGORY_GENERAL, "terraformMult", 1f, "Multplier for atmosphere change speed").getDouble(); + arConfig.lavaCentrifugeOutputs = config.getStringList("lavaCentrifugeOutputs", Configuration.CATEGORY_GENERAL, new String[]{"nuggetCopper:100", "nuggetIron:100", "nuggetTin:100", "nuggetLead:100", "nuggetSilver:100", "nuggetGold:75", "nuggetDiamond:10", "nuggetUranium:10", "nuggetIridium:1"}, "List of Centrifuge outputs from enriched lava. Format: :."); + arConfig.lavaCentrifugePower = config.getInt("lavaCentrifugePower", Configuration.CATEGORY_GENERAL, 10,0,999999,"Power per tick, used to process enriched lava."); + arConfig.lavaCentrifugeTime = config.getInt("lavaCentrifugeTime", Configuration.CATEGORY_GENERAL, 50,0,999999,"Time used to process 250 mB of enriched lava."); + arConfig.crystalliserMaximumGravity = (float) config.get(Configuration.CATEGORY_GENERAL, "crystalliserMaximumGravity", 0f, "Maximum gravity where the crystalliser works. Set 0.0 to disable.").getDouble(); + arConfig.enableLaserDrill = config.get(Configuration.CATEGORY_GENERAL, "EnableLaserDrill", true, "Enable the laser drill.").getBoolean(); + arConfig.spaceLaserPowerMult = (float) config.get(Configuration.CATEGORY_GENERAL, "LaserDrillPowerMultiplier", 1d, "Multiplier for laser drill power use.").getDouble(); + arConfig.laserDrillPlanet = config.get(Configuration.CATEGORY_GENERAL, "laserDrillPlanet", false, "If true, laser drill mines blocks below, (false makes it a VoidMiner with improved performance! especially when using Void Cobble: ON in GUI)").getBoolean(); + String[] str = config.getStringList("spaceLaserDimIdBlackList", Configuration.CATEGORY_GENERAL, new String[]{}, "Dimensions where the laser drill cannot mine."); + arConfig.enableTerraforming = config.get(Configuration.CATEGORY_GENERAL, "EnableTerraforming", true, "Enable terraforming blocks and items.").getBoolean(); + arConfig.terraformSpeed = config.get(Configuration.CATEGORY_GENERAL, "terraformMult", 1f, "Multiplier for atmosphere change speed").getDouble(); //arConfig.terraformPlanetSpeed = config.get(Configuration.CATEGORY_GENERAL, "terraformBlockPerTick", 1, "Max number of blocks allowed to be changed per tick").getInt(); - arConfig.terraformRequiresFluid = config.get(Configuration.CATEGORY_GENERAL, "TerraformerRequiresFluids", true, "Whether the Terraformer should consume fluids at all, independent of rate").getBoolean(); - arConfig.terraformliquidRate = config.get(Configuration.CATEGORY_GENERAL, "TerraformerFluidConsumeRate", 40, "how many millibuckets/t are required to keep the terraformer running").getInt(); - arConfig.allowTerraformNonAR = config.get(Configuration.CATEGORY_GENERAL, "allowTerraformingNonARWorlds", false, "If true dimensions not added by AR can be terraformed, including the overworld").getBoolean(); - arConfig.enableGravityController = config.get(Configuration.CATEGORY_GENERAL, "enableGravityMachine", true, "If false the gravity controller cannot be built or used").getBoolean(); - arConfig.allowNonArBiomesInTerraforming = config.get(Configuration.CATEGORY_GENERAL, "allowNonArBiomesInTerraforming", false, "non-ar biomes from mods with custom world gen can not be decorated in terraforming. If you want fully decorated terraforming with only default biomes, set this to false").getBoolean(); + arConfig.terraformRequiresFluid = config.get(Configuration.CATEGORY_GENERAL, "TerraformerRequiresFluids", true, "Require fluids to run the Terraformer.").getBoolean(); + arConfig.terraformliquidRate = config.get(Configuration.CATEGORY_GENERAL, "TerraformerFluidConsumeRate", 40, "mB/t used by the Terraformer.").getInt(); + arConfig.allowTerraformNonAR = config.get(Configuration.CATEGORY_GENERAL, "allowTerraformingNonARWorlds", false, "Allow terraforming in non-AR dimensions, including the overworld.").getBoolean(); + arConfig.enableGravityController = config.get(Configuration.CATEGORY_GENERAL, "enableGravityMachine", true, "Enable the gravity controller.").getBoolean(); + arConfig.allowNonArBiomesInTerraforming = config.get(Configuration.CATEGORY_GENERAL, "allowNonArBiomesInTerraforming", false, "non-AR biomes from mods with custom world gen cannot be decorated in terraforming. If you want fully decorated terraforming with only default biomes, set this to false").getBoolean(); + arConfig.enableOrbitalRegistry = config.get(Configuration.CATEGORY_GENERAL,"EnableOrbitalRegistry",true, "Enable the orbital registry.").getBoolean(); + + //Oxygen - arConfig.enableOxygen = config.get(OXYGEN, "EnableAtmosphericEffects", true, "If true, allows players being hurt due to lack of oxygen and allows effects from non-standard atmosphere types").getBoolean(); - AtmosphereVacuum.damageValue = config.get(OXYGEN, "vacuumDamage", 1, "Amount of damage taken every second in a vacuum").getInt(); - arConfig.overrideGCAir = config.get(OXYGEN, "OverrideGCAir", true, "If true Galacticcraft's air will be disabled entirely requiring use of Advanced Rocketry's Oxygen system on GC planets").getBoolean(); - arConfig.oxygenVentConsumptionMult = config.get(OXYGEN, "oxygenVentConsumptionMultiplier", 1f, "Multiplier on how much O2 an oxygen vent consumes per tick").getDouble(); - arConfig.oxygenVentPowerMultiplier = config.get(OXYGEN, "OxygenVentPowerMultiplier", 1.0f, "Power consumption multiplier for the oxygen vent", 0, Float.MAX_VALUE).getDouble(); - arConfig.spaceSuitOxygenTime = config.get(OXYGEN, "spaceSuitO2Buffer", 30, "Maximum time in minutes that the spacesuit's internal buffer can store O2 for").getInt(); - arConfig.suitTankCapacity = (float) config.get(OXYGEN, "suitTankCapacity", 1.0f, "Global multiplier for suit extra tank capacity", 0, Float.MAX_VALUE).getDouble(); - arConfig.scrubberRequiresCartrige = config.get(OXYGEN, "scrubberRequiresCartrige", true, "If true the Oxygen scrubbers require a consumable carbon collection cartridge").getBoolean(); - arConfig.dropExTorches = config.get(OXYGEN, "dropExtinguishedTorches", false, "If true, breaking an extinguished torch will drop an extinguished torch instead of a vanilla torch").getBoolean(); - sealableBlockWhiteList = config.getStringList("sealableBlockWhiteList", OXYGEN, new String[]{}, "Blocks that are not automatically detected as sealable but should seal. Format \"Mod:Blockname\" for example \"minecraft:chest\""); - sealableBlockBlackList = config.getStringList("sealableBlockBlackList", OXYGEN, new String[]{}, "Blocks that are automatically detected as sealable but should not seal. Format \"Mod:Blockname\" for example \"minecraft:chest\""); - breakableTorches = config.getStringList("torchBlocks", OXYGEN, new String[]{}, "Mod:Blockname for example \"minecraft:chest\""); - entityList = config.getStringList("entityAtmBypass", OXYGEN, new String[]{}, "list entities which should not be affected by atmosphere properties"); + arConfig.enableOxygen = config.get(OXYGEN, "EnableAtmosphericEffects", true, "Enable damage from lack of oxygen and effects from non-standard atmospheres.").getBoolean(); + AtmosphereVacuum.damageValue = config.get(OXYGEN, "vacuumDamage", 1, "Damage taken per second in a vacuum.").getInt(); + arConfig.overrideGCAir = config.get(OXYGEN, "OverrideGCAir", true, "Disable Galacticraft air and use AR oxygen on GC planets.").getBoolean(); + arConfig.oxygenVentConsumptionMult = config.get(OXYGEN, "oxygenVentConsumptionMultiplier", 1f, "Multiplier for oxygen vent O2 use per tick.").getDouble(); + arConfig.oxygenVentPowerMultiplier = config.get(OXYGEN, "OxygenVentPowerMultiplier", 1.0f, "Multiplier for oxygen vent power use.", 0, Float.MAX_VALUE).getDouble(); + arConfig.spaceSuitOxygenTime = config.get(OXYGEN, "spaceSuitO2Buffer", 30, "Maximum suit O2 buffer time in minutes.").getInt(); + arConfig.suitTankCapacity = (float) config.get(OXYGEN, "suitTankCapacity", 1.0f, "Multiplier for suit extra tank capacity.", 0, Float.MAX_VALUE).getDouble(); + arConfig.scrubberRequiresCartrige = config.get(OXYGEN, "scrubberRequiresCartrige", true, "Require cartridges for oxygen scrubbers.").getBoolean(); + arConfig.dropExTorches = config.get(OXYGEN, "dropExtinguishedTorches", false, "Drop an extinguished torch instead of a vanilla torch, when breaking an extinguished torch.").getBoolean(); + sealableBlockWhiteList = config.getStringList("sealableBlockWhiteList", OXYGEN, new String[]{}, "Blocks that should count as sealable. Format: modid:block for example \"minecraft:chest\""); + sealableBlockBlackList = config.getStringList("sealableBlockBlackList", OXYGEN, new String[]{}, "Blocks that should not count as sealable. Format: modid:block for example \"minecraft:chest\""); + breakableTorches = config.getStringList("torchBlocks", OXYGEN, new String[]{}, "Blocks treated like torches in non-combustible atmospheres. Placement is blocked, and existing blocks are broken and dropped. Format: modid:block"); + entityList = config.getStringList("entityAtmBypass", OXYGEN, new String[]{}, "List entities not affected by atmosphere effects"); //Station - arConfig.spaceDimId = config.get(STATION, "spaceStationId", -2, "Dimension ID to use for space stations").getInt(); - arConfig.stationSize = config.get(STATION, "SpaceStationBuildRadius", 1024, "The largest size a space station can be. Should also be a power of 2 (512, 1024, 2048, 4096, ...). CAUTION: CHANGING THIS OPTION WILL DAMAGE EXISTING STATIONS!!!").getInt(); - arConfig.allowZeroGSpacestations = config.get(STATION, "allowZeroGSpacestations", false, "If true players will be able to completely disable gravity on spacestation. It's possible to get stuck and require a teleport, you have been warned!").getBoolean(); - arConfig.fuelPointsPerDilithium = config.get(STATION, "pointsPerDilithium", 500, "How many units of fuel should each Dilithium Crystal give to warp ships", 1, 1000).getInt(); + arConfig.spaceDimId = config.get(STATION, "spaceStationId", -2, "Dimension ID used for space stations.").getInt(); + arConfig.stationSize = config.get(STATION, "SpaceStationBuildRadius", 1024, "Maximum space station build radius. Should be a power of 2 (512, 1024, 2048, 4096, ...). CAUTION: CHANGING THIS OPTION WILL DAMAGE EXISTING STATIONS!!!").getInt(); + arConfig.allowZeroGSpacestations = config.get(STATION, "allowZeroGSpacestations", false, "Allow stations to fully disable gravity. It's possible to get stuck and require teleport, you have been warned!").getBoolean(); + arConfig.fuelPointsPerDilithium = config.get(STATION, "pointsPerDilithium", 500, "Warp fuel units provided by each Dilithium Crystal.", 1, 1000).getInt(); arConfig.travelTimeMultiplier = (float) config.get(STATION, "warpTravelTime", 1f, "Multiplier for warp travel time").getDouble(); //Missions - arConfig.asteroidMiningTimeMult = config.get(MISSION, "miningMissionTmeMultiplier", 1.0, "Multiplier changing how long a mining mission takes").getDouble(); - arConfig.gasCollectionMult = config.get(MISSION, "gasMissionMultiplier", 1.0, "Multiplier for the amount of time gas collection missions take").getDouble(); - harvestableGasses = config.getStringList("harvestableGasses", MISSION, new String[]{}, "list of fluid names that can be harvested as Gas from any gas giant"); - spawnableGasses = config.getStringList("spawnableGasses", MISSION, new String[]{"hydrogen;125;1600;1.0", "helium;125;1600;0.9", "helium3;175;1600;0.2", "oxygen;0;124;1.0", "nitrogen;0;124;1.0", "ammonia;0;124;0.75", "methane;0;124;0.25"}, "list of fluid names that can be spawned as a gas giant. Format is fluid;minGravity;maxGravity;chance"); + arConfig.asteroidMiningTimeMult = config.get(MISSION, "miningMissionTmeMultiplier", 1.0, "Multiplier for mining mission time.").getDouble(); + arConfig.gasCollectionMult = config.get(MISSION, "gasMissionMultiplier", 1.0, "Multiplier for gas mission time.").getDouble(); + harvestableGasses = config.getStringList("harvestableGasses", MISSION, new String[]{}, "List of fluid names that can be harvested from any gas giant"); + spawnableGasses = config.getStringList("spawnableGasses", MISSION, new String[]{"hydrogen;125;1600;1.0", "helium;125;1600;0.9", "helium3;175;1600;0.2", "oxygen;0;124;1.0", "nitrogen;0;124;1.0", "ammonia;0;124;0.75", "methane;0;124;0.25"}, "List of fluids that can generate on gas giants. Format: fluid;minGravity;maxGravity;chance"); + arConfig.gasHarvestAmountMultiplier = config.get( + MISSION, "gasHarvestAmountMultiplier", 1.0, + "Per-mission harvest cap = 64,000 mB × multiplier. Ignored if gasHarvestInfinite=true." + ).getDouble(); + + arConfig.gasHarvestInfinite = config.get( + MISSION, "gasHarvestInfinite", false, + "True sets gasHarvestAmount = MaxInt (2,147,483,647 mB), and ignores the 'gasHarvestAmountMultiplier'" + ).getBoolean(); + //Energy Production - arConfig.solarGeneratorMult = config.get(ENERGY, "solarGeneratorMultiplier", 1, "Amount of power per tick the solar generator should produce").getInt(); - arConfig.microwaveRecieverMulitplier = (float) config.get(ENERGY, "MicrowaveRecieverMultiplier", 1f, "Multiplier for the amount of energy produced by the microwave reciever").getDouble(); - arConfig.defaultItemTimeBlackHole = config.get(ENERGY, "defaultBurnTime", 500, "List of blocks and the amount of ticks they can power the black hole generator format: 'modname:block:meta;number_of_ticks'").getInt(); - arConfig.blackHolePowerMultiplier = config.get(ENERGY, "blackHoleGeneratorMultiplier", 1, "Multiplier for the amount of power per tick the black hole generator should produce").getInt(); - blackHoleGeneratorTiming = config.get(ENERGY, "blackHoleTimings", new String[]{"minecraft:stone;1", "minecraft:dirt;1", "minecraft:netherrack;1", "minecraft:cobblestone;1"}, "List of blocks and the amount of ticks they can power the black hole generator format: 'modname:block:meta;number_of_ticks'").getStringList(); + arConfig.solarGeneratorMult = config.get(ENERGY, "solarGeneratorMultiplier", 1, "Power produced per tick by the solar generator.").getInt(); + arConfig.microwaveRecieverMulitplier = (float) config.get(ENERGY, "MicrowaveRecieverMultiplier", 1f, "Multiplier for microwave receiver power output.").getDouble(); + arConfig.defaultItemTimeBlackHole = config.get(ENERGY, "defaultBurnTime", 500, "Burn time in ticks for items not listed in blackHoleTimings.").getInt(); + arConfig.blackHolePowerMultiplier = config.get(ENERGY, "blackHoleGeneratorMultiplier", 1, "Multiplier for black hole generator power output.").getInt(); + blackHoleGeneratorTiming = config.get(ENERGY, "blackHoleTimings", new String[]{"minecraft:stone;1", "minecraft:dirt;1", "minecraft:netherrack;1", "minecraft:cobblestone;1"}, "List of blocks and burn times for the black hole generator. Format: modid:block:meta;ticks where meta is optional").getStringList(); //Planet - arConfig.planetsMustBeDiscovered = config.get(PLANET, "planetsMustBeDiscovered", false, "If true planets must be discovered in the warp controller before being visible").getBoolean(); - arConfig.planetDiscoveryChance = config.get(PLANET, "planetDiscoveryChance", 5, "Chance of planet discovery in the warp ship monitor is not all planets are initially discoved, chance is 1/n", 1, Integer.MAX_VALUE).getInt(); - boolean resetResetFromXml = config.getBoolean("ResetOnlyOnce", PLANET, true, "setting this to false will will prevent resetPlanetsFromXML from being set to false upon world reload. Recommended for those who want to force ALL saves to ALWAYS use the planetDefs XML in the /config folder. Essentially that 'Are you sure you're sure' option. If resetPlanetsFromXML is false, this option does nothing."); + arConfig.planetsMustBeDiscovered = config.get(PLANET, "planetsMustBeDiscovered", false, "Planets must be discovered in the warp controller before being visible").getBoolean(); + arConfig.planetDiscoveryChance = config.get(PLANET, "planetDiscoveryChance", 5, "Chance of planet discovery in the warp controller, chance is 1/n", 1, Integer.MAX_VALUE).getInt(); + boolean resetResetFromXml = config.getBoolean("ResetOnlyOnce", PLANET, true, "Setting this to false will prevent resetPlanetsFromXML from being set to false upon world reload. Recommended for those who want to force ALL saves to ALWAYS use the planetDefs XML in the /config folder. Essentially that 'Are you sure you're sure' option. If resetPlanetsFromXML is false, this option does nothing."); //Reset to false if (resetResetFromXml) - config.get(PLANET, "resetPlanetsFromXML", false, "Whether the planets should be reset from the config XML on this restart").set(false); - DimensionManager.dimOffset = config.getInt("minDimension", PLANET, 2, -127, 8000, "Dimensions including and after this number are allowed to be made into planets"); - arConfig.canPlayerRespawnInSpace = config.get(PLANET, "allowPlanetRespawn", false, "If true players will respawn near beds on planets IF the spawn location is in a breathable atmosphere").getBoolean(); - arConfig.forcePlayerRespawnInSpace = config.get(PLANET, "forcePlanetRespawn", false, "If true players will respawn near beds on planets REGARDLESS of the spawn location being in a non-breathable atmosphere. Requires 'allowPlanetRespawn' being true.").getBoolean(); - arConfig.blackListAllVanillaBiomes = config.getBoolean("blackListVanillaBiomes", PLANET, false, "Prevents any vanilla biomes from spawning on planets"); - arConfig.maxBiomesPerPlanet = config.get(PLANET, "maxBiomesPerPlanet", 99, "Maximum unique biomes per planet").getInt(); + config.get(PLANET, "resetPlanetsFromXML", false, "Reload planet definitions from config XML on this restart.").set(false); + DimensionManager.dimOffset = config.getInt("minDimension", PLANET, 2, -127, 8000, "Lowest dimension ID that can be used for planets."); + arConfig.canPlayerRespawnInSpace = config.get(PLANET, "allowPlanetRespawn", false, "Allow bed respawn on planets with breathable air.").getBoolean(); + arConfig.forcePlayerRespawnInSpace = config.get(PLANET, "forcePlanetRespawn", false, "Allow bed respawn on planets even without breathable air. Requires 'allowPlanetRespawn=true'.").getBoolean(); + arConfig.blackListAllVanillaBiomes = config.getBoolean("blackListVanillaBiomes", PLANET, false, "Prevent vanilla biomes from spawning on planets."); + arConfig.maxBiomesPerPlanet = config.get(PLANET, "maxBiomesPerPlanet", 99, "Maximum unique biomes per planet.").getInt(); //Client - arConfig.stationSkyOverride = config.get(CLIENT, "StationSkyOverride", true, "If true, AR will use a custom skybox on space stations").getBoolean(); - arConfig.planetSkyOverride = config.get(CLIENT, "PlanetSkyOverride", true, "If true, AR will use a custom skybox on planets").getBoolean(); - arConfig.skyOverride = config.get(CLIENT, "overworldSkyOverride", true).getBoolean(); + arConfig.stationSkyOverride = config.get(CLIENT, "StationSkyOverride", true, "Use AR's custom skybox on space stations").getBoolean(); + arConfig.planetSkyOverride = config.get(CLIENT, "PlanetSkyOverride", true, "Use AR's custom skybox on planets").getBoolean(); + arConfig.skyOverride = config.get(CLIENT, "overworldSkyOverride", true, "Use AR's custom skybox in the overworld.").getBoolean(); // arConfig.overworldsealevelterraforming = config.get(CLIENT, "overworldSealvlTerraforming", true).getBoolean(); arConfig.advancedVFX = config.get(CLIENT, "advancedVFX", true, "Advanced visual effects").getBoolean(); - arConfig.enableNausea = config.get(CLIENT, "EnableAtmosphericNausea", true, "If true, allows players to experience nausea on non-standard atmosphere types").getBoolean(); + arConfig.enableNausea = config.get(CLIENT, "EnableAtmosphericNausea", true, "Allows nausea effects in non-standard atmospheres.").getBoolean(); arConfig.electricPlantsSpawnLightning = config.get(CLIENT, "electricPlantsSpawnLightning", true, "Should Electric Mushrooms be able to spawn lightning").getBoolean(); //Performance @@ -447,31 +471,31 @@ public static void loadPreInit() { arConfig.oxygenVentSize = config.get(PERFORMANCE, "oxygenVentSize", 32, "Radius of the O2 vent. if atmosphereCalculationMethod is 2 or 3 then max volume is calculated from this radius. WARNING: larger numbers can lead to lag").getInt(); //Rockets - arConfig.rocketRequireFuel = config.get(ROCKET, "rocketsRequireFuel", true, "Set to false if rockets should not require fuel to fly").getBoolean(); - arConfig.canBeFueledByHand = config.get(ROCKET, "canBeFueledByHand", true, "Set to false if rockets should not be able to be fueled by and and will require a fueling station").getBoolean(); - liquidMonopropellant = config.get(ROCKET, "rocketFuels", new String[]{"rocketfuel;10"}, "List of fluid names for fluids that can be used as rocket monopropellants").getStringList(); - liquidBipropellantFuel = config.get(ROCKET, "rocketBipropellants", new String[]{"hydrogen;10"}, "List of fluid names for fluids that can be used as rocket bipropellant fuels").getStringList(); - liquidBipropellantOxidizer = config.get(ROCKET, "rocketOxidizers", new String[]{"oxygen;10"}, "List of fluid names for fluids that can be used as rocket bipropellant oxidizers").getStringList(); - liquidNuclearWorkingFluid = config.get(ROCKET, "rocketNuclearWorkingFluids", new String[]{"hydrogen;10"}, "List of fluid names for fluids that can be used as rocket nuclear working fluids").getStringList(); - arConfig.rocketThrustMultiplier = config.get(ROCKET, "thrustMultiplier", 1f, "Multiplier for per-engine thrust").getDouble(); - arConfig.fuelCapacityMultiplier = config.get(ROCKET, "fuelCapacityMultiplier", 1f, "Multiplier for per-tank capacity").getDouble(); - arConfig.nuclearCoreThrustRatio = config.get(ROCKET, "nuclearCoreThrustRatio", 1.0, "The multiplier for the thrust of the nuclear core block. With default configuration, this value provides a (max) thrust of 1000 per core.").getDouble(); + arConfig.rocketRequireFuel = config.get(ROCKET, "rocketsRequireFuel", true, "Require fuel for rockets to fly.").getBoolean(); + arConfig.canBeFueledByHand = config.get(ROCKET, "canBeFueledByHand", true, "Allow rockets to be fueled by hand.").getBoolean(); + arConfig.nuclearRocketsRespectArtifactGating = config.get(ROCKET, "nuclearRocketsRespectArtifactGating", true, "Nuclear rocket should respect artifact gating for planets").getBoolean(); + liquidMonopropellant = config.get(ROCKET, "rocketFuels", new String[]{"rocketfuel;10"}, "List of fluid names for valid monopropellants").getStringList(); + liquidBipropellantFuel = config.get(ROCKET, "rocketBipropellants", new String[]{"hydrogen;10"}, "List of fluid names for valid bipropellant fuels").getStringList(); + liquidBipropellantOxidizer = config.get(ROCKET, "rocketOxidizers", new String[]{"oxygen;10"}, "List of fluid names for valid bipropellant oxidizers").getStringList(); + liquidNuclearWorkingFluid = config.get(ROCKET, "rocketNuclearWorkingFluids", new String[]{"hydrogen;10"}, "List of fluid names for valid nuclear working fluids").getStringList(); + arConfig.rocketThrustMultiplier = config.get(ROCKET, "thrustMultiplier", 1f, "Multiplier for engine thrust.").getDouble(); + arConfig.fuelCapacityMultiplier = config.get(ROCKET, "fuelCapacityMultiplier", 1f, "Multiplier for fuel tank capacity.").getDouble(); + arConfig.nuclearCoreThrustRatio = config.get(ROCKET, "nuclearCoreThrustRatio", 1.0, "Multiplier for nuclear core thrust.").getDouble(); arConfig.automaticRetroRockets = config.get(ROCKET, "autoRetroRockets", true, "Setting to false will disable the retrorockets that fire automatically on reentry on both player and automated rockets").getBoolean(); - arConfig.orbit = config.getInt("orbitHeight", ROCKET, 1000, 255, Integer.MAX_VALUE, "How high the rocket has to go before it reaches orbit. This is used by itself when launching from a planet to LEO, which can be either a satellite, a space station, or another point on this planet's surface. It's used in conjunction with the TBI burn when launching to the moon or asteroids. Warp flights will need orbit height + 10x TBI to launch from planets"); - arConfig.stationClearanceHeight = config.getInt("stationClearance", ROCKET, 1000, 255, Integer.MAX_VALUE, "How high the rocket has to go before it clears a space station and can enter its own orbit - WARNING: This property is not synced with orbitHeight and so will be displayed incorrectly on monitors if not equal to it. Burn length here is used by itself when launching from a station to either another station or the same station, or to the planet it is orbiting. it is used in conjunction with the TBI burn when launching to a moon or asteroid"); + arConfig.orbit = config.getInt("orbitHeight", ROCKET, 1000, 255, Integer.MAX_VALUE, "Height required to reach orbit.. This is used by itself when launching from a planet to LEO, which can be either a satellite, a space station, or another point on this planet's surface. It's used in conjunction with the TBI burn when launching to the moon or asteroids. Warp flights will need orbit height + 10x TBI to launch from planets"); + arConfig.stationClearanceHeight = config.getInt("stationClearance", ROCKET, 1000, 255, Integer.MAX_VALUE, "Height required to clear a space station. WARNING: This property is not synced with orbitHeight and so will be displayed incorrectly on monitors if not equal to it. Burn length here is used by itself when launching from a station to either another station or the same station, or to the planet it is orbiting. It is used in conjunction with the TBI burn when launching to a moon or asteroid"); arConfig.transBodyInjection = config.getInt("transBodyInjection", ROCKET, 0, 0, Integer.MAX_VALUE, "How long the burn for trans-body injection is - this is performed soley after entering orbit and is in blocks - WARNING: This property is not taken into account by any machines when determining whether the rocket is fit to fly or not - Rockets that can reach LEO and so are flightworthy may not make TBI and will fall back to the parent planet. When enabled, the burn sequence is [Burn to LEO], [TBI Burn] when launching from a planet to moons or asteroids; and the sequence is [Station clearance burn], [TBI Burn] when launching from a station to a moon or asteroid. This distance varies by object distance"); - arConfig.asteroidTBIBurnMult = (float) config.get(ROCKET, "asteroidTBIBurnMult", 1.0, "The multiplier that asteroids should be considered as for TBI distance").getDouble(); - arConfig.warpTBIBurnMult = (float) config.get(ROCKET, "warpTBIBurnMult", 10.0, "The multiplier that warp rocket flights should be considered as for TBI distance").getDouble(); - arConfig.experimentalSpaceFlight = config.get(ROCKET, "experimentalSpaceFlight", false, "If true, rockets will be able to actually fly around space, EXPERIMENTAL").getBoolean(); - arConfig.gravityAffectsFuel = config.get(ROCKET, "gravityAffectsFuels", true, "If true planets with higher gravity require more fuel and lower gravity would require less").getBoolean(); - arConfig.launchingDestroysBlocks = config.get(ROCKET, "launchBlockDestruction", false, "If true rocket launches will kill plants, glass soil, turn rock into lava, and more").getBoolean(); - blackListRocketBlocksStr = config.getStringList("rocketBlockBlackList", ROCKET, new String[]{"minecraft:portal", "minecraft:bedrock", "minecraft:snow_layer", "minecraft:water", "minecraft:flowing_water", "minecraft:lava", "minecraft:flowing_lava", "minecraft:fire", "advancedrocketry:rocketfire"}, "Mod:Blockname for example \"minecraft:chest\""); - arConfig.advancedWeightSystem = config.get(ROCKET, "advancedWeightSystem", true, "Enables advanced weight system which computes rocket weight, including the handled inventories. Block weights are stores in weights.json").getBoolean(); - arConfig.advancedWeightSystemInventories = config.get(ROCKET, "advancedWeightSystemInventories", true, "Enables advanced weight system for inventories - may not work with modded inventories (eg IE storage chests)").getBoolean(); - arConfig.partsWearSystem = config.get(ROCKET, "partsWearSystem", true, "Enables rocket parts wear subsystem. Every rocket start it has probability to explode based on parts' wear intensities").getBoolean(); - arConfig.increaseWearIntensityProb = config.get(ROCKET, "increaseWearIntensityProb", 0.025, "Every rocket usage every part has this probability to increase wear intensity").getDouble(); - - //Ore and worldgen configuration + arConfig.asteroidTBIBurnMult = (float) config.get(ROCKET, "asteroidTBIBurnMult", 1.0, "Multiplier for asteroid TBI distance.").getDouble(); + arConfig.warpTBIBurnMult = (float) config.get(ROCKET, "warpTBIBurnMult", 10.0, "Multiplier for warp TBI distance.").getDouble(); + arConfig.experimentalSpaceFlight = config.get(ROCKET, "experimentalSpaceFlight", false, "Enable EXPERIMENTAL free flight in space.").getBoolean(); + arConfig.gravityAffectsFuel = config.get(ROCKET, "gravityAffectsFuels", true, "Make fuel use depend on gravity.").getBoolean(); + arConfig.launchingDestroysBlocks = config.get(ROCKET, "launchBlockDestruction", false, "Allow launches to damage nearby blocks, plants, glass, soil, turn rock into lava, and more").getBoolean(); + blackListRocketBlocksStr = config.getStringList("rocketBlockBlackList", ROCKET, new String[]{"minecraft:portal", "minecraft:bedrock", "minecraft:snow_layer", "minecraft:water", "minecraft:flowing_water", "minecraft:lava", "minecraft:flowing_lava", "minecraft:fire", "advancedrocketry:rocketfire"}, "Blocks that cannot be part of rocket. Format: modid:block e.g \"minecraft:chest\""); + arConfig.advancedWeightSystem = config.get(ROCKET, "advancedWeightSystem", true, "Enable advanced rocket weight calculation, including the handled inventories. Block weights are stored in weights.json").getBoolean(); + arConfig.advancedWeightSystemInventories = config.get(ROCKET, "advancedWeightSystemInventories", true, "Include inventory contents in rocket weight. Note: may not work with modded inventories (eg IE storage chests)").getBoolean(); + arConfig.partsWearSystem = config.get(ROCKET, "partsWearSystem", true, "Enable rocket part wear and exploding chance.").getBoolean(); + arConfig.increaseWearIntensityProb = config.get(ROCKET, "increaseWearIntensityProb", 0.025, "Chance for each part to gain wear on launch.").getDouble(); + //Ore configuration final boolean masterToggle = arConfig.generateCopper = config.get(WORLDGEN, "EnableOreGen", true).getBoolean(); arConfig.generateCopper = config.get(WORLDGEN, "GenerateCopper", true).getBoolean() && masterToggle; @@ -494,19 +518,18 @@ public static void loadPreInit() { arConfig.IridiumClumpSize = config.get(WORLDGEN, "IridiumPerClump", 16).getInt(); arConfig.IridiumPerChunk = config.get(WORLDGEN, "IridiumPerChunk", 1).getInt(); //Orbital laser - arConfig.laserDrillOresBlackList = config.get(WORLDGEN, "laserDrillOres_blacklist", true, "True if the ores in laserDrillOres should be a blacklist, false for whitelist. if set to false in combination with empty ore list it will crash the game").getBoolean(); - orbitalLaserOres = config.get(WORLDGEN, "laserDrillOres", new String[]{}, "List of oredictionary names of ores allowed to be mined by the laser drill if surface drilling is disabled. Ores can be specified by just the oreName: or by ::: where size is optional").getStringList(); + arConfig.laserDrillOresBlackList = config.get(WORLDGEN, "laserDrillOres_blacklist", true, "Treat laserDrillOres as a blacklist. Note: false + empty ore list will crash the game").getBoolean(); + orbitalLaserOres = config.get(WORLDGEN, "laserDrillOres", new String[]{}, "List of ores allowed to be mined by the laser drill if surface drilling is disabled. Ores can be specified by just the oreName: (oredict) or by modid:block:meta: where size is stacksize and optional").getStringList(); //Geode - arConfig.geodeOresBlackList = config.get(WORLDGEN, "geodeOres_blacklist", false, "True if the ores in geodeOres should be a blacklist, false for whitelist").getBoolean(); - arConfig.generateGeodes = config.get(WORLDGEN, "generateGeodes", true, "If true then ore-containing geodes are generated on high pressure planets").getBoolean(); - arConfig.geodeBaseSize = config.get(WORLDGEN, "geodeBaseSize", 36, "average size of the geodes").getInt(); - arConfig.geodeVariation = config.get(WORLDGEN, "geodeVariation", 24, "variation in geode size").getInt(); - geodeOres = config.get(WORLDGEN, "geodeOres", new String[]{"oreIron", "oreGold", "oreCopper", "oreTin", "oreRedstone"}, "List of oredictionary names of ores allowed to spawn in geodes").getStringList(); + arConfig.geodeOresBlackList = config.get(WORLDGEN, "geodeOres_blacklist", false, "Treat geodeOres as a blacklist.").getBoolean(); + arConfig.generateGeodes = config.get(WORLDGEN, "generateGeodes", true, "Generate ore-containing geodes on high-pressure planets.").getBoolean(); + arConfig.geodeBaseSize = config.get(WORLDGEN, "geodeBaseSize", 36, "Average geode size.").getInt(); + arConfig.geodeVariation = config.get(WORLDGEN, "geodeVariation", 24, "Geode size variation.").getInt(); + geodeOres = config.get(WORLDGEN, "geodeOres", new String[]{"oreIron", "oreGold", "oreCopper", "oreTin", "oreRedstone"}, "List of ores allowed in geodes. (oredict names)").getStringList(); //Other structures - arConfig.generateCraters = config.get(WORLDGEN, "generateCraters", true, "If true then low pressure planets will have meteor craters. Note: setting this option to false overrides 'generageCraters' in the planetDefs.xml").getBoolean(); - arConfig.generateVolcanos = config.get(WORLDGEN, "generateVolcanos", true, "If true then very hot planets planets will volcanos. Note: setting this option to false overrides 'generateVolcanos' in the planetDefs.xml").getBoolean(); - arConfig.generateVanillaStructures = config.getBoolean("generateVanillaStructures", WORLDGEN, false, "Enable to allow structures like villages and mineshafts to generate on planets with a breathable atmosphere. Note, setting this to false will override 'generateStructures' in the planetDefs.xml"); - + arConfig.generateCraters = config.get(WORLDGEN, "generateCraters", true, "Generate meteor craters on low-pressure planets. Note: setting this option to false overrides 'generateCraters' in the planetDefs.xml").getBoolean(); + arConfig.generateVolcanos = config.get(WORLDGEN, "generateVolcanos", true, "Generate volcanoes on very hot planets. Note: setting this option to false overrides 'generateVolcanos' in the planetDefs.xml").getBoolean(); + arConfig.generateVanillaStructures = config.getBoolean("generateVanillaStructures", WORLDGEN, false, "Allow vanilla structures on planets with breathable air. Note: setting this to false will override 'generateStructures' in the planetDefs.xml"); //Load laser dimid blacklists for (String s : str) { diff --git a/src/main/java/zmaster587/advancedRocketry/api/AdvancedRocketryBlocks.java b/src/main/java/zmaster587/advancedRocketry/api/AdvancedRocketryBlocks.java index ad05c0b22..f56dd0119 100644 --- a/src/main/java/zmaster587/advancedRocketry/api/AdvancedRocketryBlocks.java +++ b/src/main/java/zmaster587/advancedRocketry/api/AdvancedRocketryBlocks.java @@ -109,4 +109,6 @@ public class AdvancedRocketryBlocks { public static Block blockRocketFire; public static Block blockServiceMonitor; public static Block blockInvHatch; + public static Block blockOrbitalRegistry; + public static Block blockDataBusBig; } diff --git a/src/main/java/zmaster587/advancedRocketry/api/Constants.java b/src/main/java/zmaster587/advancedRocketry/api/Constants.java index 807b35876..07c1a6666 100644 --- a/src/main/java/zmaster587/advancedRocketry/api/Constants.java +++ b/src/main/java/zmaster587/advancedRocketry/api/Constants.java @@ -2,6 +2,7 @@ public class Constants { public static final String modId = "advancedrocketry"; + public static final String DEPENDENCIES = "required-after:libvulpes@[0.5.0,);"; public static final int INVALID_PLANET = Integer.MIN_VALUE + 1; //min value is used for warp public static final int GENTYPE_ASTEROID = 2; public static final int STAR_ID_OFFSET = 10000; diff --git a/src/main/java/zmaster587/advancedRocketry/api/DataStorage.java b/src/main/java/zmaster587/advancedRocketry/api/DataStorage.java index 5cb5dc963..1693d93b1 100644 --- a/src/main/java/zmaster587/advancedRocketry/api/DataStorage.java +++ b/src/main/java/zmaster587/advancedRocketry/api/DataStorage.java @@ -20,16 +20,25 @@ public DataStorage(DataType data) { } public boolean setData(int data, DataType dataType) { - if (this.dataType == DataStorage.DataType.UNDEFINED) + // If empty/typeless, allow adopting the provided type (unless locked) + if (!this.locked && this.dataType == DataType.UNDEFINED && dataType != DataType.UNDEFINED) { this.dataType = dataType; + } + + if (dataType == DataType.UNDEFINED || dataType == this.dataType) { + this.data = Math.max(0, Math.min(data, maxData)); - if (dataType == DataStorage.DataType.UNDEFINED || dataType == this.dataType) { - this.data = Math.min(data, maxData); + // If we just became empty and are not locked, clear type + if (!this.locked && this.data == 0) { + this.dataType = DataType.UNDEFINED; + } return true; } return false; } + + public int getData() { return data; } @@ -78,20 +87,46 @@ public boolean isLocked() { * @param dataType type to add * @return data amount added */ - public int addData(int data, DataType dataType, boolean commit) { - if ((!this.locked && (dataType == DataStorage.DataType.UNDEFINED)) || dataType == this.dataType || this.dataType == DataStorage.DataType.UNDEFINED) { + public int addData(int data, DataType incomingType, boolean commit) { + // Snapshot + final boolean empty = (this.data == 0); + DataType current = this.dataType; + + // Compute effective type without mutating state on simulation + DataType effective = current; + if (!this.locked && empty) { + effective = (incomingType != DataType.UNDEFINED) ? incomingType : DataType.UNDEFINED; + } + + // Accept if: unlocked+UNDEFINED (wildcard), same type, or typeless + boolean accepts = + (!this.locked && incomingType == DataType.UNDEFINED) || + (incomingType == effective) || + (!this.locked && effective == DataType.UNDEFINED); + + + if (!accepts) return 0; - if (this.dataType == DataStorage.DataType.UNDEFINED) - this.dataType = dataType; + int amountToAdd = Math.min(data, this.maxData - this.data); + if (amountToAdd <= 0) return 0; - int amountToAdd = Math.min(data, this.maxData - this.data); - if (commit) - this.data += amountToAdd; - return amountToAdd; + if (commit) { + // Finalize adoption only on real add + if (!this.locked && this.data == 0) { + this.dataType = (incomingType != DataType.UNDEFINED) ? incomingType : DataType.UNDEFINED; + } + this.data += amountToAdd; + + // If still UNDEFINED but we added concrete data (edge case), solidify + if (this.dataType == DataType.UNDEFINED && incomingType != DataType.UNDEFINED) { + this.dataType = incomingType; + } } - return 0; + return amountToAdd; } + + /** * @param data max amount of data to remove * @return amount of data removed @@ -122,12 +157,15 @@ public void readFromNBT(NBTTagCompound nbt) { dataType = DataType.UNDEFINED; } - - ///TODO: dev compat if (nbt.hasKey("locked")) locked = nbt.getBoolean("locked"); else locked = false; + + // >>> ADD: heal stale type on empty <<< + if (!locked && data == 0) { + dataType = DataType.UNDEFINED; + } } public enum DataType { diff --git a/src/main/java/zmaster587/advancedRocketry/api/EntityRocketBase.java b/src/main/java/zmaster587/advancedRocketry/api/EntityRocketBase.java index 685b5ae2e..ea5440d79 100644 --- a/src/main/java/zmaster587/advancedRocketry/api/EntityRocketBase.java +++ b/src/main/java/zmaster587/advancedRocketry/api/EntityRocketBase.java @@ -3,6 +3,7 @@ import net.minecraft.entity.Entity; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; +import zmaster587.advancedRocketry.AdvancedRocketry; import zmaster587.advancedRocketry.api.fuel.FuelRegistry; import zmaster587.advancedRocketry.api.stations.ISpaceObject; import zmaster587.libVulpes.util.HashedBlockPosition; @@ -140,8 +141,16 @@ public void onOrbitReached() { /** * Deconstructs the rocket, replacing it with actual blocks + * Log and continue even if event handlers throw exceptions */ public void deconstructRocket() { - MinecraftForge.EVENT_BUS.post(new RocketEvent.RocketDismantleEvent(this)); + try { + MinecraftForge.EVENT_BUS.post(new RocketEvent.RocketDismantleEvent(this)); + } catch (Throwable t) { + AdvancedRocketry.logger.error( + "RocketDismantleEvent handler threw for rocket {}, continuing deconstruction anyway", + this, t + ); + } } } diff --git a/src/main/java/zmaster587/advancedRocketry/api/RocketEvent.java b/src/main/java/zmaster587/advancedRocketry/api/RocketEvent.java index e4c3644fc..a93b79b26 100644 --- a/src/main/java/zmaster587/advancedRocketry/api/RocketEvent.java +++ b/src/main/java/zmaster587/advancedRocketry/api/RocketEvent.java @@ -35,6 +35,13 @@ public RocketPreLaunchEvent(Entity entity) { super(entity); } } + public static class RocketAbortEvent extends RocketEvent { + public final String reason; // optional, for GUIs/logs + public RocketAbortEvent(Entity entity, String reason) { + super(entity); + this.reason = reason; + } + } /** * Fired before the rocket is finished teleporting to the destination world on the Minecraft Forge EVENT_BUS diff --git a/src/main/java/zmaster587/advancedRocketry/api/StatsRocket.java b/src/main/java/zmaster587/advancedRocketry/api/StatsRocket.java index 427f01ee4..83beafcd4 100644 --- a/src/main/java/zmaster587/advancedRocketry/api/StatsRocket.java +++ b/src/main/java/zmaster587/advancedRocketry/api/StatsRocket.java @@ -145,6 +145,10 @@ public float getWeight() { Fluid f = FluidRegistry.getFluid(getOxidizerFluid()); fluidWeight += WeightEngine.INSTANCE.getWeight(f, getFuelAmount(FuelType.LIQUID_OXIDIZER)); } + if (FluidRegistry.isFluidRegistered(getWorkingFluid())) { + Fluid f = FluidRegistry.getFluid(getWorkingFluid()); + fluidWeight += WeightEngine.INSTANCE.getWeight(f, getFuelAmount(FuelType.NUCLEAR_WORKING_FLUID)); + } } return weight + fluidWeight; } @@ -498,7 +502,6 @@ public void setFuelCapacity(@Nonnull FuelRegistry.FuelType type, int amt) { * @return amount of fuel added */ public int addFuelAmount(@Nonnull FuelRegistry.FuelType type, int amt) { - //TODO: finish other ones switch (type) { case WARP: int maxAddWarp = fuelCapacityWarp - fuelWarp; @@ -554,6 +557,7 @@ public void reset() { weight = 0; fuelFluid = "null"; oxidizerFluid = "null"; + workingFluid = "null"; drillingPower = 0f; for (FuelType type : FuelType.values()) { @@ -720,12 +724,12 @@ public void readFromNBT(NBTTagCompound nbt) { this.fuelRateNuclearWorkingFluid = stats.getInteger("fuelRateNuclearWorkingFluid"); this.fuelRateWarp = stats.getInteger("fuelRateWarp"); - this.fuelBaseRateMonopropellant = stats.getInteger("fuelBaseRateMonopropellant"); - this.fuelBaseRateBipropellant = stats.getInteger("fuelBaseRateBipropellant"); - this.fuelBaseRateOxidizer = stats.getInteger("fuelBaseRateOxidizer"); + this.fuelBaseRateMonopropellant = (int)stats.getFloat("fuelBaseRateMonopropellant"); + this.fuelBaseRateBipropellant = (int)stats.getFloat("fuelBaseRateBipropellant"); + this.fuelBaseRateOxidizer = (int)stats.getFloat("fuelBaseRateOxidizer"); this.fuelBaseRateImpulse = stats.getInteger("fuelBaseRateImpulse"); this.fuelBaseRateIon = stats.getInteger("fuelBaseRateIon"); - this.fuelBaseRateNuclearWorkingFluid = stats.getInteger("fuelBaseRateNuclearWorkingFluid"); + this.fuelBaseRateNuclearWorkingFluid = (int)stats.getFloat("fuelBaseRateNuclearWorkingFluid"); this.fuelBaseRateWarp = stats.getInteger("fuelBaseRateWarp"); @@ -766,4 +770,4 @@ else if (obj instanceof NBTTagInt) } } } -} \ No newline at end of file +} diff --git a/src/main/java/zmaster587/advancedRocketry/api/fuel/FuelRegistry.java b/src/main/java/zmaster587/advancedRocketry/api/fuel/FuelRegistry.java index 1bf1cef8c..2e44a3a67 100644 --- a/src/main/java/zmaster587/advancedRocketry/api/fuel/FuelRegistry.java +++ b/src/main/java/zmaster587/advancedRocketry/api/fuel/FuelRegistry.java @@ -113,7 +113,7 @@ public enum FuelType { */ public boolean addFuel(@Nonnull FuelEntry entry) { entry.type = this; - return !fuels.add(entry); + return fuels.add(entry); } /** @@ -143,7 +143,7 @@ private boolean isFuel(@Nullable Object obj) { return false; for (FuelEntry fuel : fuels) { - if (fuel.fuel == obj) + if (fuel.fuelMatches(obj)) return true; } @@ -203,14 +203,20 @@ public FuelEntry(@Nonnull Object fuel, float multiplier) { * @return true if the passed object is indeed the same fuel */ public boolean fuelMatches(@Nullable Object obj) { - if (obj == null || fuel.getClass() != obj.getClass()) + if (obj == null) return false; - else if (fuel instanceof ItemStack) { + + if (fuel instanceof ItemStack && obj instanceof ItemStack) { return ItemStack.areItemStacksEqual((ItemStack) fuel, (ItemStack) obj); - } else if (fuel instanceof Fluid) { - return fuel.equals(obj); - } else + } else if (fuel instanceof Fluid && obj instanceof Fluid) { + Fluid a = (Fluid) fuel; + Fluid b = (Fluid) obj; + String an = a.getName(); + String bn = b.getName(); + return a == b || (an != null && an.equals(bn)); + } else { return false; + } } //Override equals(Object), each the itemstack or fluid determines the entry @@ -225,5 +231,22 @@ public boolean equals(@Nullable Object obj) { } return false; } + @Override + public int hashCode() { + int result = (type != null ? type.hashCode() : 0); + + if (fuel instanceof ItemStack) { + ItemStack stack = (ItemStack) fuel; + result = 31 * result + stack.getItem().hashCode(); + result = 31 * result + stack.getMetadata(); + result = 31 * result + (stack.hasTagCompound() ? stack.getTagCompound().hashCode() : 0); + } else if (fuel instanceof Fluid) { + Fluid fluid = (Fluid) fuel; + String name = fluid.getName(); + result = 31 * result + (name != null ? name.hashCode() : 0); + } + + return result; + } } } \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/asm/AdvancedRocketryPlugin.java b/src/main/java/zmaster587/advancedRocketry/asm/AdvancedRocketryPlugin.java index d991a2128..345cf2039 100644 --- a/src/main/java/zmaster587/advancedRocketry/asm/AdvancedRocketryPlugin.java +++ b/src/main/java/zmaster587/advancedRocketry/asm/AdvancedRocketryPlugin.java @@ -3,24 +3,18 @@ import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.MCVersion; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions; -import zmaster587.advancedRocketry.ARHookLoader; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.minecraft.HookLoader; import java.util.Map; @TransformerExclusions(value = {"zmaster587.advancedRocketry.asm.ClassTransformer"}) @MCVersion("1.12.2") public class AdvancedRocketryPlugin implements IFMLLoadingPlugin { - - private final HookLoader hookLoader; - public AdvancedRocketryPlugin() { - hookLoader = new ARHookLoader(); } @Override public String[] getASMTransformerClass() { - return new String[]{ClassTransformer.class.getName(), hookLoader.getASMTransformerClass()[0]}; + return new String[]{ClassTransformer.class.getName()}; } @Override @@ -30,16 +24,15 @@ public String getModContainerClass() { @Override public String getSetupClass() { - return hookLoader.getSetupClass(); + return null; } @Override public void injectData(Map data) { - hookLoader.injectData(data); } @Override public String getAccessTransformerClass() { - return hookLoader.getAccessTransformerClass(); + return null; } } diff --git a/src/main/java/zmaster587/advancedRocketry/asm/ClassTransformer.java b/src/main/java/zmaster587/advancedRocketry/asm/ClassTransformer.java index c9fb8e7a2..1c0dbab08 100644 --- a/src/main/java/zmaster587/advancedRocketry/asm/ClassTransformer.java +++ b/src/main/java/zmaster587/advancedRocketry/asm/ClassTransformer.java @@ -686,17 +686,15 @@ public byte[] transform(String name, String transformedName, LabelNode label = new LabelNode(); AbstractInsnNode pos; AbstractInsnNode ain = null; - int numSpec = 1; - int numAload = 7; for (int i = 0; i < onUpdate.instructions.size(); i++) { ain = onUpdate.instructions.get(i); - if (ain.getOpcode() == Opcodes.INVOKESPECIAL && numSpec-- == 0) { - + if (ain.getOpcode() == Opcodes.GETFIELD && ((FieldInsnNode) ain).name.equals(obf ? "by" : "openContainer")) { + ain = ain.getPrevious(); while (i < onUpdate.instructions.size()) { pos = onUpdate.instructions.get(i++); - if (pos.getOpcode() == Opcodes.ALOAD && numAload-- == 0) { - label = (LabelNode) pos.getPrevious().getPrevious().getPrevious(); + if (pos.getOpcode() == Opcodes.PUTFIELD && ((FieldInsnNode) pos).name.equals(obf ? "by" : "openContainer")) { + label = (LabelNode) pos.getNext(); break; } @@ -711,7 +709,7 @@ public byte[] transform(String name, String transformedName, nodeAdd.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "zmaster587/advancedRocketry/util/RocketInventoryHelper", "allowAccess", "(Ljava/lang/Object;)Z", false)); nodeAdd.add(new JumpInsnNode(Opcodes.IFEQ, label)); - onUpdate.instructions.insert(ain, nodeAdd); + onUpdate.instructions.insertBefore(ain, nodeAdd); //onUpdate.instructions.insertBefore(pos, label); diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockBipropellantRocketMotor.java b/src/main/java/zmaster587/advancedRocketry/block/BlockBipropellantRocketMotor.java index 74d3d4a6b..e9342d566 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockBipropellantRocketMotor.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockBipropellantRocketMotor.java @@ -2,6 +2,9 @@ import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; @@ -11,14 +14,20 @@ import net.minecraft.util.EnumFacing; import net.minecraft.util.NonNullList; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.IRocketEngine; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.tile.TileBrokenPart; import zmaster587.advancedRocketry.util.IBrokenPartBlock; import zmaster587.libVulpes.block.BlockFullyRotatable; +import java.util.List; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -39,6 +48,23 @@ public int getThrust(World world, BlockPos pos) { return 10; } + @Override + public IBlockState getActualState(@Nonnull IBlockState state, IBlockAccess world, BlockPos pos) { + if (world.getBlockState(pos.up()).getBlock() instanceof BlockFuelTank) + return state.withProperty(FACING, EnumFacing.DOWN); + if (world.getBlockState(pos.down()).getBlock() instanceof BlockFuelTank) + return state.withProperty(FACING, EnumFacing.UP); + if (world.getBlockState(pos.east()).getBlock() instanceof BlockFuelTank) + return state.withProperty(FACING, EnumFacing.EAST); + if (world.getBlockState(pos.west()).getBlock() instanceof BlockFuelTank) + return state.withProperty(FACING, EnumFacing.WEST); + if (world.getBlockState(pos.south()).getBlock() instanceof BlockFuelTank) + return state.withProperty(FACING, EnumFacing.SOUTH); + if (world.getBlockState(pos.north()).getBlock() instanceof BlockFuelTank) + return state.withProperty(FACING, EnumFacing.NORTH); + return super.getActualState(state, world, pos); + } + @Override public int getFuelConsumptionRate(World world, int x, int y, int z) { return 1; @@ -102,6 +128,13 @@ public TileEntity createTileEntity(final World worldIn, final IBlockState state) return new TileBrokenPart(10, (float) ARConfiguration.getCurrentConfig().increaseWearIntensityProb); } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.bipropmotor", insertAt); + } + @Override public ItemStack getDropItem(final IBlockState state, final World world, final @Nullable TileBrokenPart te) { ItemStack drop = new ItemStack(this.getItemDropped(state, world.rand, 0)); diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockDoor2.java b/src/main/java/zmaster587/advancedRocketry/block/BlockDoor2.java index e6012eb75..cd569c4cf 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockDoor2.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockDoor2.java @@ -3,6 +3,7 @@ import net.minecraft.block.BlockDoor; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.item.Item; @@ -14,9 +15,13 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.AdvancedRocketryItems; +import zmaster587.advancedRocketry.client.TooltipInjector; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNullableByDefault; + +import java.util.List; import java.util.Random; public class BlockDoor2 extends BlockDoor { @@ -32,7 +37,6 @@ public ItemStack getItem(World worldIn, BlockPos pos, IBlockState state) { return new ItemStack(AdvancedRocketryItems.itemSmallAirlockDoor); } - @Override @Nonnull public Item getItemDropped(IBlockState state, Random rand, int fortune) { @@ -45,5 +49,5 @@ public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) { return false; - } + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockForceFieldProjector.java b/src/main/java/zmaster587/advancedRocketry/block/BlockForceFieldProjector.java index 59958642e..19405aa88 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockForceFieldProjector.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockForceFieldProjector.java @@ -1,10 +1,18 @@ package zmaster587.advancedRocketry.block; +import java.util.List; +import javax.annotation.Nullable; + import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.tile.TileForceFieldProjector; import zmaster587.libVulpes.block.BlockFullyRotatable; @@ -33,5 +41,10 @@ public void breakBlock(World worldIn, BlockPos pos, IBlockState state) { public TileEntity createTileEntity(World world, IBlockState state) { return new TileForceFieldProjector(); } - + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.forcefieldprojector", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockIntake.java b/src/main/java/zmaster587/advancedRocketry/block/BlockIntake.java index 706f2f883..af0b222e0 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockIntake.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockIntake.java @@ -1,19 +1,35 @@ package zmaster587.advancedRocketry.block; +import java.util.List; + +import javax.annotation.Nullable; + import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.IIntake; +import zmaster587.advancedRocketry.client.TooltipInjector; public class BlockIntake extends Block implements IIntake { public BlockIntake(Material material) { super(material); } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.intake", insertAt); + } @Override public int getIntakeAmt(IBlockState state) { - return 10; + return 1; } - } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockLandingPad.java b/src/main/java/zmaster587/advancedRocketry/block/BlockLandingPad.java index 4eb73f072..d559f1835 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockLandingPad.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockLandingPad.java @@ -1,14 +1,26 @@ package zmaster587.advancedRocketry.block; +import java.util.List; + +import javax.annotation.Nullable; + import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.tile.station.TileLandingPad; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.inventory.GuiHandler; @@ -48,6 +60,13 @@ public boolean onBlockActivated(World world, BlockPos pos, return true; } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.landingpad", insertAt); + } + @Override public void breakBlock(World world, BlockPos pos, IBlockState state) { TileEntity tile = world.getTileEntity(pos); diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockLens.java b/src/main/java/zmaster587/advancedRocketry/block/BlockLens.java index 6807ad386..4e71391ad 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockLens.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockLens.java @@ -1,8 +1,18 @@ package zmaster587.advancedRocketry.block; +import java.util.List; + +import javax.annotation.Nullable; + import net.minecraft.block.BlockGlass; import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; /** * Yes, this class may seem useless, but setSoundType can't be run in the registry, only by a subclass of Block. @@ -12,4 +22,11 @@ public BlockLens() { super(Material.GLASS, true); setSoundType(SoundType.GLASS); } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.lens", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockLinkedHorizontalTexture.java b/src/main/java/zmaster587/advancedRocketry/block/BlockLinkedHorizontalTexture.java index 7e9153b17..aee2caf69 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockLinkedHorizontalTexture.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockLinkedHorizontalTexture.java @@ -1,13 +1,25 @@ package zmaster587.advancedRocketry.block; +import java.util.List; + +import javax.annotation.Nullable; + import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.properties.PropertyEnum; import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; import net.minecraft.util.IStringSerializable; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; public class BlockLinkedHorizontalTexture extends Block { @@ -84,4 +96,43 @@ public String getName() { return suffix; } } -} \ No newline at end of file + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + tooltip.add(TextFormatting.GRAY + I18n.format("tooltip.advancedrocketry.launchpad")); + + final boolean shift = GuiScreen.isShiftKeyDown(); + final boolean alt = isAltDown(); + + if (alt) { + // Advanced details + tooltip.add(TextFormatting.DARK_GRAY + I18n.format("tooltip.advancedrocketry.launchpad.alt.1")); + tooltip.add(TextFormatting.DARK_GRAY + I18n.format("tooltip.advancedrocketry.launchpad.alt.2")); + } else if (shift) { + // More info + tooltip.add(TextFormatting.GRAY + I18n.format("tooltip.advancedrocketry.launchpad.shift.1")); + tooltip.add(TextFormatting.GRAY + I18n.format("tooltip.advancedrocketry.launchpad.shift.2")); + if (I18n.hasKey("tooltip.advancedrocketry.hold_alt")) + tooltip.add(TextFormatting.DARK_GRAY.toString() + TextFormatting.ITALIC + I18n.format("tooltip.advancedrocketry.hold_alt")); + } else { + // Hints + if (I18n.hasKey("tooltip.advancedrocketry.hold_shift")) + tooltip.add(TextFormatting.DARK_GRAY.toString() + TextFormatting.ITALIC + I18n.format("tooltip.advancedrocketry.hold_shift")); + if (I18n.hasKey("tooltip.advancedrocketry.hold_alt")) + tooltip.add(TextFormatting.DARK_GRAY.toString() + TextFormatting.ITALIC + I18n.format("tooltip.advancedrocketry.hold_alt")); + } + } + + @SideOnly(Side.CLIENT) + private static boolean isAltDown() { + try { + // Works on Forge 1.12.x; LWJGL fallback for safety + return GuiScreen.isAltKeyDown() + || org.lwjgl.input.Keyboard.isKeyDown(org.lwjgl.input.Keyboard.KEY_LMENU) + || org.lwjgl.input.Keyboard.isKeyDown(org.lwjgl.input.Keyboard.KEY_RMENU); + } catch (Throwable t) { + return false; + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockMiningDrill.java b/src/main/java/zmaster587/advancedRocketry/block/BlockMiningDrill.java index 432bf6dac..13b4fc1db 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockMiningDrill.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockMiningDrill.java @@ -1,10 +1,19 @@ package zmaster587.advancedRocketry.block; +import java.util.List; + +import javax.annotation.Nullable; + import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.IMiningDrill; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.libVulpes.block.BlockFullyRotatable; public class BlockMiningDrill extends BlockFullyRotatable implements IMiningDrill { @@ -24,9 +33,15 @@ public float getMiningSpeed(World world, BlockPos pos) { return world.isAirBlock(pos.add(0, 1, 0)) && world.isAirBlock(pos.add(0, 2, 0)) ? 0.02f : 0.01f; } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.drill", insertAt); + } + @Override public int powerConsumption() { return 0; } - } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockNuclearCore.java b/src/main/java/zmaster587/advancedRocketry/block/BlockNuclearCore.java index 7cdc3d25b..1664ad679 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockNuclearCore.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockNuclearCore.java @@ -2,10 +2,21 @@ import net.minecraft.block.Block; import net.minecraft.block.material.Material; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.IRocketNuclearCore; +import zmaster587.advancedRocketry.client.TooltipInjector; + +import javax.annotation.Nullable; +import java.util.List; public class BlockNuclearCore extends Block implements IRocketNuclearCore { @@ -18,5 +29,10 @@ public int getMaxThrust(World world, BlockPos pos) { return (int) (1000 * ARConfiguration.getCurrentConfig().nuclearCoreThrustRatio); } - + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.nuclearcore", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockNuclearRocketMotor.java b/src/main/java/zmaster587/advancedRocketry/block/BlockNuclearRocketMotor.java index ed84d34d5..604f1321b 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockNuclearRocketMotor.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockNuclearRocketMotor.java @@ -2,12 +2,26 @@ import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.ARConfiguration; +import zmaster587.advancedRocketry.api.IRocketNuclearCore; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.tile.TileBrokenPart; +import java.util.List; + +import javax.annotation.Nonnull; import javax.annotation.Nullable; public class BlockNuclearRocketMotor extends BlockRocketMotor { @@ -20,7 +34,23 @@ public BlockNuclearRocketMotor(Material mat) { public int getThrust(World world, BlockPos pos) { return 35; } - + @Override + public IBlockState getActualState(@Nonnull IBlockState state, IBlockAccess world, BlockPos pos) { + // Prefer nuclear core adjacency over tanks + if (world.getBlockState(pos.up()).getBlock() instanceof IRocketNuclearCore) + return state.withProperty(FACING, EnumFacing.DOWN); + if (world.getBlockState(pos.down()).getBlock() instanceof IRocketNuclearCore) + return state.withProperty(FACING, EnumFacing.UP); + if (world.getBlockState(pos.east()).getBlock() instanceof IRocketNuclearCore) + return state.withProperty(FACING, EnumFacing.EAST); + if (world.getBlockState(pos.west()).getBlock() instanceof IRocketNuclearCore) + return state.withProperty(FACING, EnumFacing.WEST); + if (world.getBlockState(pos.south()).getBlock() instanceof IRocketNuclearCore) + return state.withProperty(FACING, EnumFacing.SOUTH); + if (world.getBlockState(pos.north()).getBlock() instanceof IRocketNuclearCore) + return state.withProperty(FACING, EnumFacing.NORTH); + return state; + } @Override public int getFuelConsumptionRate(World world, int x, int y, int z) { return 1; @@ -31,4 +61,11 @@ public int getFuelConsumptionRate(World world, int x, int y, int z) { public TileEntity createTileEntity(final World worldIn, final IBlockState state) { return new TileBrokenPart(10, 4 * (float) ARConfiguration.getCurrentConfig().increaseWearIntensityProb); } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.nuclearmotor", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockPressurizedFluidTank.java b/src/main/java/zmaster587/advancedRocketry/block/BlockPressurizedFluidTank.java index cd6dffcfa..53cc745a2 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockPressurizedFluidTank.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockPressurizedFluidTank.java @@ -15,7 +15,6 @@ import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.fluids.FluidUtil; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; @@ -48,66 +47,136 @@ public boolean hasTileEntity(IBlockState state) { } @Override - public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) { - TileEntity tile = world.getTileEntity(pos); - - //Do some fancy fluid stuff - if (FluidUtils.containsFluid(player.getHeldItem(hand))) { - FluidUtil.interactWithFluidHandler(player, hand, ((TileFluidHatch) tile).getFluidTank()); - } else if (!world.isRemote) - player.openGui(LibVulpes.instance, guiId.MODULAR.ordinal(), world, pos.getX(), pos.getY(), pos.getZ()); + public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, + EnumFacing side, float hitX, float hitY, float hitZ) { + TileEntity te = world.getTileEntity(pos); + if (!(te instanceof TileFluidTank)) return false; + + // Client: consume the click (let server do the actual transfer) + if (world.isRemote) return true; + + // Try to interact via the tile's FLUID CAPABILITY (column-aware path), + // NOT the raw internal tank. + IFluidHandler handler = te.getCapability( + net.minecraftforge.fluids.capability.CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, + side + ); + if (handler == null) { + handler = te.getCapability( + net.minecraftforge.fluids.capability.CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, + null + ); + } + + boolean acted = false; + if (handler != null) { + // Server-side inventory mutation + acted = net.minecraftforge.fluids.FluidUtil.interactWithFluidHandler(player, hand, handler); + if (acted) { + TileFluidTank tank = (TileFluidTank) te; + // Persist + sync + tank.markDirty(); + tank.onAdjacentBlockUpdated(EnumFacing.DOWN); + tank.onAdjacentBlockUpdated(EnumFacing.UP); + } + } + + // If we didn't perform a fluid interaction, open the GUI + if (!acted) { + player.openGui(zmaster587.libVulpes.LibVulpes.instance, + zmaster587.libVulpes.inventory.GuiHandler.guiId.MODULAR.ordinal(), + world, pos.getX(), pos.getY(), pos.getZ()); + } return true; } + + + @Override + public void onBlockAdded(World world, BlockPos pos, IBlockState state) { + super.onBlockAdded(world, pos, state); + if (world.isRemote) return; + TileEntity teAbove = world.getTileEntity(pos.up()); + if (teAbove instanceof TileFluidTank) { + ((TileFluidTank) teAbove).onAdjacentBlockUpdated(EnumFacing.DOWN); + } + } + + @Override @ParametersAreNullableByDefault public TileEntity createTileEntity(World world, IBlockState state) { - return new TileFluidTank((int) (64000 * ARConfiguration.getCurrentConfig().blockTankCapacity)); + long computed = Math.round(64000d * ARConfiguration.getCurrentConfig().blockTankCapacity); + int capMb = (int) Math.min(Integer.MAX_VALUE, Math.max(0L, computed)); + return new TileFluidTank(capMb); } @Override @Nonnull @ParametersAreNullableByDefault - public List getDrops(IBlockAccess world, BlockPos pos, - IBlockState state, int fortune) { - return new LinkedList<>(); - } + public List getDrops(IBlockAccess world, BlockPos pos, IBlockState state, int fortune) { + List drops = new LinkedList<>(); + TileEntity te = world.getTileEntity(pos); - @Override - @ParametersAreNonnullByDefault - public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, @Nullable TileEntity te, @Nonnull ItemStack stack) { + ItemStack out = new ItemStack(AdvancedRocketryBlocks.blockPressureTank); if (te instanceof TileFluidTank) { - IFluidHandler fluid = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, EnumFacing.DOWN); - + net.minecraftforge.fluids.FluidStack own = ((TileFluidTank) te).getOwnContentsCopy(); + if (own != null && own.amount > 0) { + ((ItemBlockFluidTank) out.getItem()).fill(out, own); + } + } - ItemStack itemstack = new ItemStack(AdvancedRocketryBlocks.blockPressureTank); + drops.add(out); + return drops; + } - ((ItemBlockFluidTank) itemstack.getItem()).fill(itemstack, fluid.drain(Integer.MAX_VALUE, false)); - EntityItem entityitem; + @Override + public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest) { + if (!world.isRemote) { + TileEntity te = world.getTileEntity(pos); + if (te instanceof TileFluidTank) { + ((TileFluidTank) te).setRemoving(true); + } + } + // Let vanilla handle removal; we’ll control drops in harvestBlock + return super.removedByPlayer(state, world, pos, player, willHarvest); + } - int j1 = world.rand.nextInt(21) + 10; - float f = world.rand.nextFloat() * 0.8F + 0.1F; - float f1 = world.rand.nextFloat() * 0.8F + 0.1F; - float f2 = world.rand.nextFloat() * 0.8F + 0.1F; + @Override + public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, + @Nullable TileEntity te, @Nonnull ItemStack tool) { + if (world.isRemote) return; + + // Creative: no drop, just remove + if (player.capabilities.isCreativeMode) { + world.setBlockToAir(pos); + return; + } - itemstack.setCount(1); - entityitem = new EntityItem(world, (float) pos.getX() + f, (float) pos.getY() + f1, (float) pos.getZ() + f2, new ItemStack(itemstack.getItem(), 1, 0)); - float f3 = 0.05F; - entityitem.motionX = (float) world.rand.nextGaussian() * f3; - entityitem.motionY = (float) world.rand.nextGaussian() * f3 + 0.2F; - entityitem.motionZ = (float) world.rand.nextGaussian() * f3; + // Build ONE drop item from the authoritative server tile we received + ItemStack drop = new ItemStack(AdvancedRocketryBlocks.blockPressureTank); + if (te instanceof TileFluidTank) { + // Make sure the tile knows it's in teardown; block cross-tile moves + ((TileFluidTank) te).setRemoving(true); - if (itemstack.hasTagCompound()) { - entityitem.getItem().setTagCompound(itemstack.getTagCompound().copy()); + net.minecraftforge.fluids.FluidStack own = ((TileFluidTank) te).getOwnContentsCopy(); + if (own != null && own.amount > 0) { + ((ItemBlockFluidTank) drop.getItem()).fill(drop, own); } - world.spawnEntity(entityitem); } - super.harvestBlock(world, player, pos, state, te, stack); + EntityItem ei = new EntityItem(world, + pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, drop); + world.spawnEntity(ei); + + world.setBlockToAir(pos); } + + + @Override @ParametersAreNonnullByDefault public boolean shouldSideBeRendered(IBlockState blockState, @@ -133,12 +202,39 @@ public boolean isFullCube(IBlockState state) { return false; } + private void notifyTankOfNeighborChange(World world, BlockPos pos, BlockPos fromPos) { + // Only act for strictly adjacent vertical neighbors (no diagonals, no sides) + int dx = fromPos.getX() - pos.getX(); + int dy = fromPos.getY() - pos.getY(); + int dz = fromPos.getZ() - pos.getZ(); + + // Must be exactly one block away on Y, and same X/Z + if (dx != 0 || dz != 0) return; + if (dy != 1 && dy != -1) return; + + TileEntity te = world.getTileEntity(pos); + if (!(te instanceof zmaster587.advancedRocketry.tile.TileFluidTank)) return; + + EnumFacing dir = (dy == 1) ? EnumFacing.UP : EnumFacing.DOWN; + ((zmaster587.advancedRocketry.tile.TileFluidTank) te).onAdjacentBlockUpdated(dir); + } + + + + // Reliable for block state changes (place/break) + @Override + public void neighborChanged(IBlockState state, World world, BlockPos pos, Block blockIn, BlockPos fromPos) { + super.neighborChanged(state, world, pos, blockIn, fromPos); + if (!world.isRemote) notifyTankOfNeighborChange(world, pos, fromPos); + } + + // TE-only neighbor updates (no block state change) @Override - public void onNeighborChange(IBlockAccess world, BlockPos pos, - BlockPos neighbor) { - TileEntity tile = world.getTileEntity(pos); - if (tile instanceof TileFluidTank) - ((TileFluidTank) tile).onAdjacentBlockUpdated(EnumFacing.getFacingFromVector(neighbor.getX() - pos.getX(), neighbor.getY() - pos.getY(), neighbor.getZ() - pos.getZ())); + public void onNeighborChange(IBlockAccess world, BlockPos pos, BlockPos neighbor) { + if (world instanceof World) { + World w = (World) world; + if (!w.isRemote) notifyTankOfNeighborChange(w, pos, neighbor); + } } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockPump.java b/src/main/java/zmaster587/advancedRocketry/block/BlockPump.java new file mode 100644 index 000000000..94a888dc9 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockPump.java @@ -0,0 +1,28 @@ +package zmaster587.advancedRocketry.block; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidUtil; +import zmaster587.libVulpes.block.BlockTile; + +public class BlockPump extends BlockTile { + + public BlockPump(Class tileClass, int guiId) { + super(tileClass, guiId); + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, + EntityPlayer player, EnumHand hand, + EnumFacing side, float hitX, float hitY, float hitZ) { + // Try container <-> TE interaction first (handles buckets both directions). + if (FluidUtil.interactWithFluidHandler(player, hand, world, pos, side)) { + return true; + } + return super.onBlockActivated(world, pos, state, player, hand, side, hitX, hitY, hitZ); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockRedstoneEmitter.java b/src/main/java/zmaster587/advancedRocketry/block/BlockRedstoneEmitter.java index 99720b44b..dc4a78830 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockRedstoneEmitter.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockRedstoneEmitter.java @@ -5,20 +5,28 @@ import net.minecraft.block.properties.PropertyBool; import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.tile.atmosphere.TileAtmosphereDetector; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.inventory.GuiHandler; +import java.util.List; + import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNullableByDefault; - +// Atmosphere Detector that emits redstone signal when a specific atmosphere is detected public class BlockRedstoneEmitter extends Block { public static final PropertyBool POWERED = PropertyBool.create("powered"); @@ -86,9 +94,15 @@ public int getWeakPower(IBlockState blockState, IBlockAccess blockAccess, return blockState.getValue(POWERED) ? 15 : 0; } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.atmosphereDetector", insertAt); + } + @Override public boolean canProvidePower(IBlockState state) { return true; } - } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockRocketMotor.java b/src/main/java/zmaster587/advancedRocketry/block/BlockRocketMotor.java index 088ac702e..cba1024bc 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockRocketMotor.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockRocketMotor.java @@ -2,6 +2,9 @@ import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; @@ -11,14 +14,20 @@ import net.minecraft.util.EnumFacing; import net.minecraft.util.NonNullList; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.IRocketEngine; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.tile.TileBrokenPart; import zmaster587.advancedRocketry.util.IBrokenPartBlock; import zmaster587.libVulpes.block.BlockFullyRotatable; +import java.util.List; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -137,4 +146,11 @@ public ItemStack getDropItem(final IBlockState state, final World world, final @ } return drop; } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.monopropmotor", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockSeal.java b/src/main/java/zmaster587/advancedRocketry/block/BlockSeal.java index 894c1028a..a34786b89 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockSeal.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockSeal.java @@ -3,13 +3,18 @@ import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.AreaBlob; import zmaster587.advancedRocketry.api.util.IBlobHandler; import zmaster587.advancedRocketry.atmosphere.AtmosphereHandler; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.tile.atmosphere.TileSeal; import zmaster587.libVulpes.util.HashedBlockPosition; @@ -18,6 +23,7 @@ import javax.annotation.ParametersAreNonnullByDefault; import java.util.HashMap; import java.util.LinkedList; +import java.util.List; public class BlockSeal extends Block { @@ -196,4 +202,11 @@ public int getTraceDistance() { return -1; } } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.pipeseal", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockSeat.java b/src/main/java/zmaster587/advancedRocketry/block/BlockSeat.java index ea2676456..71d3d565c 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockSeat.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockSeat.java @@ -3,8 +3,10 @@ import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; import net.minecraft.util.BlockRenderLayer; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; @@ -13,6 +15,9 @@ import net.minecraft.world.Explosion; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.entity.EntityDummy; import javax.annotation.Nonnull; @@ -107,4 +112,11 @@ public boolean onBlockActivated(World world, BlockPos pos, return true; } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.seat", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockSolarGenerator.java b/src/main/java/zmaster587/advancedRocketry/block/BlockSolarGenerator.java index 32e58c13f..23f15865d 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockSolarGenerator.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockSolarGenerator.java @@ -1,7 +1,17 @@ package zmaster587.advancedRocketry.block; +import java.util.List; + +import javax.annotation.Nullable; + import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.libVulpes.block.BlockTile; /** @@ -22,4 +32,10 @@ public boolean isBlockNormalCube(IBlockState state) { public boolean isOpaqueCube(IBlockState state) { return true; } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.solargenerator", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockStationModuleDockingPort.java b/src/main/java/zmaster587/advancedRocketry/block/BlockStationModuleDockingPort.java index 7cec3efe0..168a9d20e 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockStationModuleDockingPort.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockStationModuleDockingPort.java @@ -2,6 +2,7 @@ import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; @@ -10,13 +11,19 @@ import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.tile.station.TileDockingPort; import zmaster587.advancedRocketry.tile.station.TileLandingPad; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.block.BlockFullyRotatable; import zmaster587.libVulpes.inventory.GuiHandler; +import java.util.List; + import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNullableByDefault; @@ -56,6 +63,13 @@ public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, } } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.dockingport", insertAt); + } + @Override @ParametersAreNonnullByDefault public void breakBlock(World world, BlockPos pos, IBlockState state) { diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockSuitWorkstation.java b/src/main/java/zmaster587/advancedRocketry/block/BlockSuitWorkstation.java index f51998463..cea44cc89 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockSuitWorkstation.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockSuitWorkstation.java @@ -1,12 +1,20 @@ package zmaster587.advancedRocketry.block; +import java.util.List; + +import javax.annotation.Nullable; + import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.item.EntityItem; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.libVulpes.block.BlockTile; public class BlockSuitWorkstation extends BlockTile { @@ -54,4 +62,11 @@ public void breakBlock(World world, BlockPos pos, IBlockState state) { world.removeTileEntity(pos); } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.suitworkingstation", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockThermiteTorch.java b/src/main/java/zmaster587/advancedRocketry/block/BlockThermiteTorch.java index 3c5aedd3a..266895ca5 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockThermiteTorch.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockThermiteTorch.java @@ -1,7 +1,23 @@ package zmaster587.advancedRocketry.block; +import java.util.List; + +import javax.annotation.Nullable; + import net.minecraft.block.BlockTorch; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; public class BlockThermiteTorch extends BlockTorch { + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.thermitetorch", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockTileNeighborUpdate.java b/src/main/java/zmaster587/advancedRocketry/block/BlockTileNeighborUpdate.java index a39b6b5c3..4ee00b0d3 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockTileNeighborUpdate.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockTileNeighborUpdate.java @@ -15,6 +15,21 @@ public BlockTileNeighborUpdate(Class tileClass, int guiId) super(tileClass, guiId); } + // redstone power uses neighbor change to update redstone power + @Override + public void neighborChanged(net.minecraft.block.state.IBlockState state, + net.minecraft.world.World world, + net.minecraft.util.math.BlockPos pos, + net.minecraft.block.Block blockIn, + net.minecraft.util.math.BlockPos fromPos) { + super.neighborChanged(state, world, pos, blockIn, fromPos); + TileEntity te = world.getTileEntity(pos); + if (te instanceof zmaster587.libVulpes.util.IAdjBlockUpdate) { + ((zmaster587.libVulpes.util.IAdjBlockUpdate) te).onAdjacentBlockUpdated(); + } + } + + @Override public void onNeighborChange(IBlockAccess world, BlockPos pos, BlockPos neighbor) { super.onNeighborChange(world, pos, neighbor); diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockTileRedstoneEmitter.java b/src/main/java/zmaster587/advancedRocketry/block/BlockTileRedstoneEmitter.java index fefcbe482..8d0604a0d 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockTileRedstoneEmitter.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockTileRedstoneEmitter.java @@ -1,24 +1,45 @@ package zmaster587.advancedRocketry.block; +import javax.annotation.Nullable; + import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.libVulpes.block.BlockTile; +import javax.annotation.Nullable; +import java.util.List; + +// Fueling Station block public class BlockTileRedstoneEmitter extends BlockTile { - public BlockTileRedstoneEmitter(Class tileClass, - int guiId) { + public BlockTileRedstoneEmitter(Class tileClass, int guiId) { super(tileClass, guiId); } @Override - public int getWeakPower(IBlockState blockState, IBlockAccess blockAccess, - BlockPos pos, EnumFacing side) { - return blockState.getValue(STATE) ? 15 : 0; + public int getWeakPower(IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing side) { + return state.getValue(STATE) ? 15 : 0; + } + + @Override + public int getStrongPower(IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing side) { + return getWeakPower(state, world, pos, side); + } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.fuelingstation", insertAt); } @Override @@ -26,11 +47,23 @@ public boolean canProvidePower(IBlockState state) { return true; } - public void setRedstoneState(World world, IBlockState state, BlockPos pos, boolean newState) { - if (world.getBlockState(pos).getBlock() != this) - return; + public void setRedstoneState(World world, IBlockState _ignored, BlockPos pos, boolean newState) { + // Server-only to avoid client mutations + if (world.isRemote) return; + + // skip if chunk isn't loaded + if (!world.isBlockLoaded(pos)) return; + + // Read the current state from the world to avoid acting on a stale IBlockState + IBlockState curState = world.getBlockState(pos); + if (curState.getBlock() != this) return; + + boolean current = curState.getValue(STATE); + if (current == newState) return; // no-op if unchanged + + IBlockState updated = curState.withProperty(STATE, newState); - world.setBlockState(pos, state.withProperty(STATE, newState)); - world.notifyBlockUpdate(pos, state, state, 3); + // 3 = neighbors notified (1) + clients updated (2) + world.setBlockState(pos, updated, 3); } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockTileTerraformer.java b/src/main/java/zmaster587/advancedRocketry/block/BlockTileTerraformer.java index 824464b51..e4967cba4 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockTileTerraformer.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockTileTerraformer.java @@ -8,6 +8,7 @@ import net.minecraft.block.properties.PropertyBool; import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; @@ -21,8 +22,12 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + import org.lwjgl.Sys; import scala.tools.nsc.doc.base.comment.EntityLink; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.advancedRocketry.dimension.DimensionProperties; import zmaster587.advancedRocketry.tile.satellite.TileTerraformingTerminal; @@ -31,7 +36,9 @@ import zmaster587.libVulpes.block.RotatableBlock; import zmaster587.libVulpes.util.IAdjBlockUpdate; +import java.util.List; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class BlockTileTerraformer extends RotatableBlock { protected Class tileClass; @@ -163,4 +170,10 @@ public void breakBlock(World world, BlockPos pos, IBlockState state) { super.breakBlock(world, pos, state); } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.terraformer", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockTransciever.java b/src/main/java/zmaster587/advancedRocketry/block/BlockTransciever.java index 38b59d4b7..9eb0d0f61 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockTransciever.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockTransciever.java @@ -1,38 +1,154 @@ package zmaster587.advancedRocketry.block; +import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraft.block.properties.PropertyDirection; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.libVulpes.block.BlockTile; +import zmaster587.advancedRocketry.client.TooltipInjector; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; public class BlockTransciever extends BlockTile { - private static final AxisAlignedBB[] bb = {new AxisAlignedBB(.25, .25, .75, .75, .75, 1), - new AxisAlignedBB(.25, .25, 0, .75, .75, 0.25), - new AxisAlignedBB(.75, .25, .25, 1, .75, .75), - new AxisAlignedBB(0, .25, .25, 0.25, .75, .75)}; + public static final PropertyDirection FACING = PropertyDirection.create("facing"); + + private static final AxisAlignedBB AABB_N = new AxisAlignedBB(.25, .25, 0.00, .75, .75, .25); + private static final AxisAlignedBB AABB_S = new AxisAlignedBB(.25, .25, .75, .75, .75, 1.00); + private static final AxisAlignedBB AABB_W = new AxisAlignedBB(0.00, .25, .25, .25, .75, .75); + private static final AxisAlignedBB AABB_E = new AxisAlignedBB(.75, .25, .25, 1.00, .75, .75); + private static final AxisAlignedBB AABB_U = new AxisAlignedBB(.25, .75, .25, .75, 1.00, .75); + private static final AxisAlignedBB AABB_D = new AxisAlignedBB(.25, 0.00, .25, .75, .25, .75); public BlockTransciever(Class tileClass, int guiId) { super(tileClass, guiId); + this.setDefaultState( + this.blockState.getBaseState() + .withProperty(STATE, Boolean.FALSE) + .withProperty(FACING, EnumFacing.NORTH) + ); + this.setHardness(3f); + this.setResistance(10f); + this.setLightOpacity(0); } @Override - public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, - BlockPos pos) { + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) { + EnumFacing f = state.getValue(FACING).getOpposite(); + switch (f) { + case NORTH: return AABB_N; + case SOUTH: return AABB_S; + case WEST: return AABB_W; + case EAST: return AABB_E; + case UP: return AABB_U; + case DOWN: return AABB_D; + default: return FULL_BLOCK_AABB; + } + } + @Override public boolean isFullCube(IBlockState state) { return false; } + @Override public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { return false; } + @Override public boolean isOpaqueCube(IBlockState state) { return false; } - return bb[state.getValue(FACING).ordinal() - 2]; + @SideOnly(Side.CLIENT) + @Override + @Nonnull + public BlockRenderLayer getBlockLayer() { + return BlockRenderLayer.CUTOUT_MIPPED; } @Override - public boolean isFullCube(IBlockState state) { - return false; + protected BlockStateContainer createBlockState() { + return new BlockStateContainer(this, FACING, STATE); + } + + @Override + public int getMetaFromState(IBlockState state) { + int face = state.getValue(FACING).getIndex(); // 0..5 + boolean on = state.getValue(STATE); + return face | (on ? 8 : 0); + } + + @Override + public IBlockState getStateFromMeta(int meta) { + EnumFacing f = EnumFacing.getFront(meta & 7); + boolean on = (meta & 8) != 0; + return this.getDefaultState().withProperty(FACING, f).withProperty(STATE, on); + } + + @Override + public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) { + return state; } @Override - public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { + @Nonnull + public IBlockState getStateForPlacement( + World world, BlockPos pos, EnumFacing clickedFace, + float hitX, float hitY, float hitZ, int meta, + EntityLivingBase placer, EnumHand hand) { + return this.getDefaultState() + .withProperty(FACING, clickedFace) + .withProperty(STATE, Boolean.FALSE); + } + + @Override + public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, + EntityLivingBase placer, @Nonnull ItemStack stack) { + // keep orientation from getStateForPlacement + } + + @Override + public boolean rotateBlock(World world, BlockPos pos, EnumFacing axis) { + IBlockState s = world.getBlockState(pos); + EnumFacing cur = s.getValue(FACING); + EnumFacing next; + + switch (axis) { + case UP: + case DOWN: + next = (cur == EnumFacing.UP || cur == EnumFacing.DOWN) ? cur : cur.rotateY(); + break; + case NORTH: + case SOUTH: + case EAST: + case WEST: + next = (cur == EnumFacing.UP || cur == EnumFacing.DOWN) ? cur : cur.rotateY(); + break; + default: + next = cur; + } + + if (next != cur) { + world.setBlockState(pos, s.withProperty(FACING, next), 2); + return true; + } return false; } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.transceiver", insertAt); + } + + public static EnumFacing getFront(IBlockState state) { + if (state == null || !state.getPropertyKeys().contains(FACING)) return EnumFacing.NORTH; + return state.getValue(FACING); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/block/BlockWarpController.java b/src/main/java/zmaster587/advancedRocketry/block/BlockWarpController.java index 6d02996fb..016e09722 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/BlockWarpController.java +++ b/src/main/java/zmaster587/advancedRocketry/block/BlockWarpController.java @@ -1,17 +1,23 @@ package zmaster587.advancedRocketry.block; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.stations.ISpaceObject; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.stations.SpaceObjectManager; import zmaster587.advancedRocketry.stations.SpaceStationObject; import zmaster587.libVulpes.block.BlockTile; +import java.util.List; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class BlockWarpController extends BlockTile { @@ -30,4 +36,12 @@ public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, ((SpaceStationObject) spaceObject).setForwardDirection(getFront(state).getOpposite()); } } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.warpcontroller", insertAt); + } + } diff --git a/src/main/java/zmaster587/advancedRocketry/block/multiblock/BlockARHatch.java b/src/main/java/zmaster587/advancedRocketry/block/multiblock/BlockARHatch.java index d45817de4..8fd68b7e7 100644 --- a/src/main/java/zmaster587/advancedRocketry/block/multiblock/BlockARHatch.java +++ b/src/main/java/zmaster587/advancedRocketry/block/multiblock/BlockARHatch.java @@ -35,17 +35,24 @@ public void getSubBlocks(CreativeTabs tab, } @Override - public boolean shouldSideBeRendered(IBlockState blockState, - IBlockAccess blockAccess, BlockPos pos, EnumFacing direction) { + public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos, EnumFacing direction) { + int variant = blockState.getValue(VARIANT); + // Always render sides for guidancecomputeraccesshatch variants (6 and 14) + if (variant == 6 || variant == 14) { + return true; + } - boolean isPointer = blockAccess.getTileEntity(pos.offset(direction.getOpposite())) instanceof TilePointer; - if (blockState.getValue(VARIANT) == 8) + // Keep + if (variant == 8) return false; - if (isPointer || blockState.getValue(VARIANT) < 2) + + boolean isPointer = blockAccess.getTileEntity(pos.offset(direction.getOpposite())) instanceof TilePointer; + if (isPointer || variant < 2) return super.shouldSideBeRendered(blockState, blockAccess, pos, direction); - return true; + + return true; } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/block/multiblock/BlockDataBusBig.java b/src/main/java/zmaster587/advancedRocketry/block/multiblock/BlockDataBusBig.java new file mode 100644 index 000000000..2bf42f37a --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/block/multiblock/BlockDataBusBig.java @@ -0,0 +1,106 @@ +package zmaster587.advancedRocketry.block.multiblock; + +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.stats.StatList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.NonNullList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import zmaster587.advancedRocketry.api.DataStorage; +import zmaster587.advancedRocketry.tile.hatch.TileDataBusBig; + +public class BlockDataBusBig extends BlockARHatch { + + public BlockDataBusBig(Material material) { + super(material); + } + + @Override + public void getSubBlocks(CreativeTabs tab, NonNullList list) { + list.add(new ItemStack(this, 1, 0)); + } + + @Override + public boolean hasTileEntity(IBlockState state) { + return true; + } + + @Override + public TileEntity createTileEntity(World world, IBlockState state) { + return new TileDataBusBig(2); + } + + /** + * Builds the dropped ItemStack. + * IMPORTANT: + * - Only attach NBT if we actually have stored data. + * This allows empty blocks to stack normally. + */ + private ItemStack makeDataStack(IBlockAccess world, BlockPos pos) { + ItemStack stack = new ItemStack(this, 1, 0); + + TileEntity te = world.getTileEntity(pos); + if (te instanceof TileDataBusBig) { + TileDataBusBig bus = (TileDataBusBig) te; + + DataStorage storage = bus.getDataObject(); + if (storage != null && storage.getData() > 0) { + NBTTagCompound tag = new NBTTagCompound(); + storage.writeToNBT(tag); + stack.setTagCompound(tag); + } + } + + return stack; + } + + @Override + public void getDrops(NonNullList drops, IBlockAccess world, BlockPos pos, + IBlockState state, int fortune) { + drops.add(makeDataStack(world, pos)); + } + + @Override + public ItemStack getItem(World worldIn, BlockPos pos, IBlockState state) { + return makeDataStack(worldIn, pos); + } + + /** + * Robust TE harvest pattern for 1.12: + * - If willHarvest, delay removal so harvestBlock can run with TE intact. + */ + @Override + public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, + EntityPlayer player, boolean willHarvest) { + if (willHarvest) { + return true; // harvestBlock will handle drops, then we remove block there + } + return super.removedByPlayer(state, world, pos, player, false); + } + + /** + * Force our custom drop and then remove the block. + * This sidesteps parent hatch drop logic. + */ + @Override + public void harvestBlock(World worldIn, EntityPlayer player, BlockPos pos, + IBlockState state, TileEntity te, ItemStack tool) { + + player.addStat(StatList.getBlockStats(this)); + player.addExhaustion(0.005F); + + if (!worldIn.isRemote) { + spawnAsEntity(worldIn, pos, makeDataStack(worldIn, pos)); + } + + // Now actually remove the block + worldIn.setBlockToAir(pos); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/client/ClientProxy.java b/src/main/java/zmaster587/advancedRocketry/client/ClientProxy.java index 6dda69fc8..1c8fc3434 100644 --- a/src/main/java/zmaster587/advancedRocketry/client/ClientProxy.java +++ b/src/main/java/zmaster587/advancedRocketry/client/ClientProxy.java @@ -50,6 +50,8 @@ import zmaster587.advancedRocketry.entity.fx.*; import zmaster587.advancedRocketry.event.PlanetEventHandler; import zmaster587.advancedRocketry.event.RocketEventHandler; +import zmaster587.advancedRocketry.inventory.modules.ModuleContainerPanYOnlyWithScrollCache; +import zmaster587.libVulpes.inventory.modules.ModuleBase; import zmaster587.advancedRocketry.stations.SpaceObjectManager; import zmaster587.advancedRocketry.tile.TileBrokenPart; import zmaster587.advancedRocketry.tile.TileFluidTank; @@ -72,6 +74,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.LinkedList; +import java.util.List; @Mod.EventBusSubscriber(value = Side.CLIENT) public class ClientProxy extends CommonProxy { @@ -479,6 +483,38 @@ protected ModelResourceLocation getModelResourceLocation(@Nullable IBlockState i } } + + @Override + public ModuleBase createScrollListPan( + int baseX, int baseY, + List list, + int sizeX, int sizeY + ) { + return new ModuleContainerPanYOnlyWithScrollCache( + baseX, baseY, + list, new LinkedList<>(), + null, + sizeX - 2, sizeY, + 0, -48, + 0, 72 + ); + } + + @Override + public void clearScrollCache() { + ModuleContainerPanYOnlyWithScrollCache.clearScrollCache(); + } + + @Override + public ModuleBase createObservatoryAsteroidListPan(int baseX, int baseY, List list2, int sizeX, int sizeY) { + return createScrollListPan(baseX, baseY, list2, sizeX, sizeY); + } + + @Override + public void clearObservatoryScrollCache() { + clearScrollCache(); + } + private static class FluidItemMeshDefinition implements ItemMeshDefinition { private final ModelResourceLocation location; diff --git a/src/main/java/zmaster587/advancedRocketry/client/KeyBindings.java b/src/main/java/zmaster587/advancedRocketry/client/KeyBindings.java index bd7a10a12..f8a59e3bb 100644 --- a/src/main/java/zmaster587/advancedRocketry/client/KeyBindings.java +++ b/src/main/java/zmaster587/advancedRocketry/client/KeyBindings.java @@ -44,6 +44,10 @@ public static void init() { ClientRegistry.registerKeyBinding(turnRocketUp); ClientRegistry.registerKeyBinding(turnRocketDown); } + //Getters for keybindings + public static KeyBinding getOpenRocketUI() { + return openRocketUI; + } @SubscribeEvent public void onKeyInput(InputEvent.KeyInputEvent event) { @@ -62,14 +66,22 @@ public void onKeyInput(InputEvent.KeyInputEvent event) { PacketHandler.sendToServer(new PacketEntity(rocket, (byte)EntityRocket.PacketType.LAUNCH.ordinal())); rocket.launch(); }*/ - + if (player.getRidingEntity() != null && player.getRidingEntity() instanceof EntityRocket) { EntityRocket rocket = (EntityRocket) player.getRidingEntity(); + /* spacehammercode : janky in large packs if (Minecraft.getMinecraft().inGameHasFocus && player.equals(Minecraft.getMinecraft().player)) { if (!rocket.isInFlight() && Keyboard.isKeyDown(Keyboard.KEY_SPACE)) { rocket.prepareLaunch(); } + */ + if (Minecraft.getMinecraft().inGameHasFocus && player.equals(Minecraft.getMinecraft().player)) { + if (!rocket.isInFlight() + && Keyboard.getEventKey() == Keyboard.KEY_SPACE + && Keyboard.getEventKeyState()) { + rocket.prepareLaunch(); + } rocket.onTurnLeft(turnRocketLeft.isKeyDown()); rocket.onTurnRight(turnRocketRight.isKeyDown()); rocket.onUp(turnRocketUp.isKeyDown()); @@ -80,8 +92,8 @@ public void onKeyInput(InputEvent.KeyInputEvent event) { if (player.getRidingEntity() != null && player.getRidingEntity() instanceof EntityHoverCraft) { EntityHoverCraft hoverCraft = (EntityHoverCraft) player.getRidingEntity(); if (Minecraft.getMinecraft().inGameHasFocus && player.equals(Minecraft.getMinecraft().player)) { - hoverCraft.onTurnLeft(turnRocketLeft.isKeyDown()); - hoverCraft.onTurnRight(turnRocketRight.isKeyDown()); + //hoverCraft.onTurnLeft(turnRocketLeft.isKeyDown()); + //hoverCraft.onTurnRight(turnRocketRight.isKeyDown()); hoverCraft.onUp(turnRocketUp.isKeyDown()); hoverCraft.onDown(turnRocketDown.isKeyDown()); } @@ -113,4 +125,4 @@ public void onKeyInput(InputEvent.KeyInputEvent event) { PacketHandler.sendToServer(new PacketChangeKeyState(Keyboard.KEY_SPACE, prevState)); } } -} \ No newline at end of file +} diff --git a/src/main/java/zmaster587/advancedRocketry/client/TooltipInjector.java b/src/main/java/zmaster587/advancedRocketry/client/TooltipInjector.java new file mode 100644 index 000000000..3498e6ad6 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/client/TooltipInjector.java @@ -0,0 +1,391 @@ +package zmaster587.advancedRocketry.client; + +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import org.lwjgl.input.Keyboard; +import zmaster587.advancedRocketry.api.Constants; +import zmaster587.advancedRocketry.api.ARConfiguration; + +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidRegistry; +import zmaster587.advancedRocketry.api.fuel.FuelRegistry; +import zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Mod.EventBusSubscriber(modid = Constants.modId, value = Side.CLIENT) +public final class TooltipInjector { + + private TooltipInjector() {} + + /** Maps exact registry IDs -> base tooltip lang key */ + private static final Map KEY_BY_ID = new HashMap<>(); + /** Fallback: maps the unlocalized-name tail -> base tooltip lang key */ + private static final Map KEY_BY_SUFFIX = new HashMap<>(); + /** Optional: dynamic args for formatted lines (usually used in .alt.2) */ + @FunctionalInterface interface ArgProvider { Object[] get(ItemStack s); } + private static final Map ARGS_BY_BASEKEY = new HashMap<>(); + /** For items that need per-stack (e.g., meta) keys */ + private static final Map> KEY_RESOLVER_BY_ID = new HashMap<>(); + + + static { + // ---- CO2 Scrubber / Oxygen Vent ---- ---- + KEY_BY_ID.put("advancedrocketry:oxygenscrubber", "tooltip.advancedrocketry.scrubber"); + KEY_BY_ID.put("advancedrocketry:oxygenvent", "tooltip.advancedrocketry.oxygenvent"); + + ARGS_BY_BASEKEY.put("tooltip.advancedrocketry.oxygenvent", + s -> new Object[] { ARConfiguration.getCurrentConfig().oxygenVentSize }); + + KEY_BY_ID.put("advancedrocketry:carbonscrubbercartridge", "tooltip.advancedrocketry.scrubbercart"); + + + // ---- Structure Tower ---- + KEY_BY_ID.put("advancedrocketry:structuretower", "tooltip.advancedrocketry.structuretower"); + KEY_BY_ID.put("libvulpes:structuremachine", "tooltip.libvulpes.structuremachine"); + KEY_BY_ID.put("libvulpes:advstructuremachine", "tooltip.libvulpes.advstructuremachine"); + + + // --- ItemUpgrade (meta-based) 6 Space Suit Components--- + KEY_RESOLVER_BY_ID.put("advancedrocketry:itemupgrade", + s -> "tooltip.advancedrocketry.itemupgrade." + s.getItemDamage()); + KEY_RESOLVER_BY_ID.put("advancedrocketry:item_upgrade", + s -> "tooltip.advancedrocketry.itemupgrade." + s.getItemDamage()); + + // ---- Guidance Computer ---- + KEY_BY_ID.put("advancedrocketry:guidancecomputer", "tooltip.advancedrocketry.guidancecomputer"); + KEY_BY_ID.put("advancedrocketry:servicemonitor", "tooltip.advancedrocketry.servicemonitor"); + KEY_BY_ID.put("advancedrocketry:servicestation", "tooltip.advancedrocketry.servicestation"); + KEY_BY_ID.put("advancedrocketry:oxygencharger", "tooltip.advancedrocketry.oxygencharger"); + + + // --- Station Controllers + KEY_BY_ID.put("advancedrocketry:orientationcontroller", "tooltip.advancedrocketry.orientationctrl"); + KEY_BY_ID.put("advancedrocketry:gravitycontroller", "tooltip.advancedrocketry.gravityctrl"); + KEY_BY_ID.put("advancedrocketry:altitudecontroller", "tooltip.advancedrocketry.altitudectrl"); + + + KEY_BY_ID.put("advancedrocketry:smallairlockdoor", "tooltip.advancedrocketry.smallairlock"); + KEY_BY_ID.put("advancedrocketry:planetselector", "tooltip.advancedrocketry.planetselector"); + KEY_BY_ID.put("advancedrocketry:planetholoselector", "tooltip.advancedrocketry.planetholoselector"); + KEY_BY_ID.put("advancedrocketry:circlelight", "tooltip.advancedrocketry.circlelight"); + KEY_BY_ID.put("advancedrocketry:monitoringstation", "tooltip.advancedrocketry.monitoringstation"); + KEY_BY_ID.put("advancedrocketry:satellitebuilder", "tooltip.advancedrocketry.satellitebuilder"); + KEY_BY_ID.put("advancedrocketry:satellitecontrolcenter", "tooltip.advancedrocketry.satellitecontrolcenter"); + + + // --- Satellite Primary Function (metas 0..6) + KEY_RESOLVER_BY_ID.put("advancedrocketry:satelliteprimaryfunction", s -> { + switch (s.getMetadata() & 7) { + case 0: return "tooltip.advancedrocketry.satfunc.optical"; + case 1: return "tooltip.advancedrocketry.satfunc.composition"; + case 2: return "tooltip.advancedrocketry.satfunc.mass"; + case 3: return "tooltip.advancedrocketry.satfunc.microwave"; + case 4: return "tooltip.advancedrocketry.satfunc.oremapping"; + case 5: return "tooltip.advancedrocketry.satfunc.biomechanger"; + case 6: return "tooltip.advancedrocketry.satfunc.weather"; + default: return null; + } + }); + // camelCase fallback + KEY_RESOLVER_BY_ID.put("advancedrocketry:satellitePrimaryFunction", + KEY_RESOLVER_BY_ID.get("advancedrocketry:satelliteprimaryfunction")); + + // --- Satellite Power Source (metas 0..1) + KEY_RESOLVER_BY_ID.put("advancedrocketry:satellitepowersource", s -> { + switch (s.getMetadata() & 1) { + case 0: return "tooltip.advancedrocketry.satpower.0"; // Basic solar + case 1: return "tooltip.advancedrocketry.satpower.1"; // Advanced solar + default: return null; + } + }); + KEY_RESOLVER_BY_ID.put("advancedrocketry:satellitePowerSource", + KEY_RESOLVER_BY_ID.get("advancedrocketry:satellitepowersource")); + + // ---- LibVulpes Battery (meta 0..1) ---- + KEY_RESOLVER_BY_ID.put("libvulpes:battery", s -> "tooltip.libvulpes.battery." + (s.getMetadata() & 1)); + + // ---- ID Chips / Chips ---- + KEY_BY_ID.put("advancedrocketry:satelliteidchip", "tooltip.advancedrocketry.satidchip"); + KEY_BY_ID.put("advancedrocketry:planetidchip", "tooltip.advancedrocketry.planetidchip"); + KEY_BY_ID.put("advancedrocketry:stationchip", "tooltip.advancedrocketry.stationchip"); + KEY_BY_ID.put("advancedrocketry:spacestationchip", "tooltip.advancedrocketry.stationchip"); + KEY_BY_ID.put("advancedrocketry:elevatorchip", "tooltip.advancedrocketry.elevatorchip"); + KEY_BY_ID.put("advancedrocketry:asteroidchip", "tooltip.advancedrocketry.asteroidchip"); + + + // ---- Energy multiblocks ---- + KEY_BY_ID.put("advancedrocketry:blackholegenerator", "tooltip.advancedrocketry.blackholegen"); + KEY_BY_ID.put("advancedrocketry:microwavereciever", "tooltip.advancedrocketry.microwavereceiver"); + KEY_BY_ID.put("advancedrocketry:solarpanel", "tooltip.advancedrocketry.solarpanel"); + KEY_BY_ID.put("advancedrocketry:solararray", "tooltip.advancedrocketry.solararray"); + KEY_BY_ID.put("advancedrocketry:solararraypanel", "tooltip.advancedrocketry.solararraypanel"); + + // Advanced Data Bus + KEY_BY_ID.put("advancedrocketry:databusbig", "tooltip.advancedrocketry.databusbig"); + // ---- BlockARHatch (registered as advancedrocketry:loader), meta 0..6 ---- + KEY_RESOLVER_BY_ID.put("advancedrocketry:loader", s -> { + final int v = s.getMetadata() & 7; // strip redstone/state bit + switch (v) { + case 0: return "tooltip.advancedrocketry.hatch.databus"; + case 1: return "tooltip.advancedrocketry.hatch.satellite"; + case 2: return "tooltip.advancedrocketry.hatch.item_unloader"; + case 3: return "tooltip.advancedrocketry.hatch.item_loader"; + case 4: return "tooltip.advancedrocketry.hatch.fluid_unloader"; + case 5: return "tooltip.advancedrocketry.hatch.fluid_loader"; + case 6: return "tooltip.advancedrocketry.hatch.gca"; + default: return null; + } + }); + + // ---- Processing / Machines / Multiblocks---- + KEY_BY_ID.put("advancedrocketry:arcfurnace", "tooltip.advancedrocketry.arcfurnace"); + KEY_BY_ID.put("advancedrocketry:rollingmachine", "tooltip.advancedrocketry.rollingmachine"); + KEY_BY_ID.put("advancedrocketry:lathe", "tooltip.advancedrocketry.lathe"); + KEY_BY_ID.put("advancedrocketry:crystallizer", "tooltip.advancedrocketry.crystallizer"); + KEY_BY_ID.put("advancedrocketry:cuttingmachine", "tooltip.advancedrocketry.cuttingmachine"); + KEY_BY_ID.put("advancedrocketry:precisionassemblingmachine", "tooltip.advancedrocketry.precisionassembler"); + KEY_BY_ID.put("advancedrocketry:electrolyser", "tooltip.advancedrocketry.electrolyser"); + KEY_BY_ID.put("advancedrocketry:chemicalreactor", "tooltip.advancedrocketry.chemreactor"); + KEY_BY_ID.put("advancedrocketry:precisionlaseretcher","tooltip.advancedrocketry.precisionlaseretcher"); + KEY_BY_ID.put("advancedrocketry:observatory", "tooltip.advancedrocketry.observatory"); + KEY_BY_ID.put("advancedrocketry:planetanalyser", "tooltip.advancedrocketry.planetanalyser"); + KEY_BY_ID.put("advancedrocketry:centrifuge", "tooltip.advancedrocketry.centrifuge"); + KEY_BY_ID.put("advancedrocketry:orbitalregistry", "tooltip.advancedrocketry.orbitalregistry"); + KEY_BY_ID.put("advancedrocketry:warpcore", "tooltip.advancedrocketry.warpcore"); + KEY_BY_ID.put("advancedrocketry:beacon", "tooltip.advancedrocketry.beacon"); + KEY_BY_ID.put("advancedrocketry:biomescanner", "tooltip.advancedrocketry.biomescan"); + KEY_BY_ID.put("advancedrocketry:railgun", "tooltip.advancedrocketry.railgun"); + KEY_BY_ID.put("advancedrocketry:spaceelevatorcontroller", "tooltip.advancedrocketry.spaceelevatorctrl"); + KEY_BY_ID.put("advancedrocketry:terraformer", "tooltip.advancedrocketry.atmosterraformer"); + KEY_BY_ID.put("advancedrocketry:gravitymachine", "tooltip.advancedrocketry.gravitymachine"); + KEY_BY_ID.put("advancedrocketry:spacelaser", "tooltip.advancedrocketry.spacelaser"); + + // ---- Building / components ---- + KEY_BY_ID.put("advancedrocketry:concrete", "tooltip.advancedrocketry.concrete"); + KEY_BY_ID.put("advancedrocketry:blastbrick", "tooltip.advancedrocketry.blastbrick"); + KEY_BY_ID.put("advancedrocketry:iquartzcrucible", "tooltip.advancedrocketry.qcrucible"); + KEY_BY_ID.put("advancedrocketry:sawblade", "tooltip.advancedrocketry.sawblade"); + KEY_BY_ID.put("advancedrocketry:vacuumlaser", "tooltip.advancedrocketry.vacuumlaser"); + + KEY_BY_ID.put("advancedrocketry:hovercraft", "tooltip.advancedrocketry.hovercraft"); + + // ---- Pump ---- + KEY_BY_ID.put("advancedrocketry:blockpump", "tooltip.advancedrocketry.pump"); + + // ---- Remotes ---- + KEY_BY_ID.put("advancedrocketry:biomechanger", "tooltip.advancedrocketry.biomechangerremote"); + KEY_BY_ID.put("advancedrocketry:weathercontroller", "tooltip.advancedrocketry.weathercontrollerremote"); + KEY_BY_ID.put("advancedrocketry:orescanner", "tooltip.advancedrocketry.orescanner"); + + + // ---- Crafting items ---- + KEY_BY_ID.put("advancedrocketry:sawbladeiron", "tooltip.advancedrocketry.sawbladeiron"); + KEY_BY_ID.put("advancedrocketry:wafer", "tooltip.advancedrocketry.wafer"); + KEY_BY_ID.put("advancedrocketry:itemcircuitplate", "tooltip.advancedrocketry.circuitplate"); + KEY_BY_ID.put("advancedrocketry:lens", "tooltip.advancedrocketry.itemlens"); + KEY_BY_ID.put("advancedrocketry:ic", "tooltip.advancedrocketry.circuitic"); + KEY_BY_ID.put("advancedrocketry:miscpart", "tooltip.advancedrocketry.miscpart"); + KEY_BY_ID.put("advancedrocketry:misc", "tooltip.advancedrocketry.misc"); + + // ---- Assemblers ---- + KEY_BY_ID.put("advancedrocketry:rocketbuilder", "tooltip.advancedrocketry.rocketassembler"); + KEY_BY_ID.put("advancedrocketry:stationbuilder", "tooltip.advancedrocketry.stationassembler"); + KEY_BY_ID.put("advancedrocketry:deployablerocketbuilder", "tooltip.advancedrocketry.deployablerocketassembler"); + + // ---- LibVulpes blocks ---- + KEY_BY_ID.put("libvulpes:coalgenerator", "tooltip.advancedrocketry.libvulpes.coalgenerator"); + KEY_BY_ID.put("libvulpes:hatch", "tooltip.advancedrocketry.libvulpes.hatch"); + KEY_BY_ID.put("libvulpes:forgepowerinput", "tooltip.advancedrocketry.libvulpes.forgepowerinput"); + KEY_BY_ID.put("libvulpes:forgepoweroutput", "tooltip.advancedrocketry.libvulpes.forgepoweroutput"); + KEY_BY_ID.put("libvulpes:creativepowerbattery", "tooltip.advancedrocketry.libvulpes.creativepowerbattery"); + + // ---- Fuel Tanks ---- + KEY_BY_ID.put("advancedrocketry:fueltank", "tooltip.advancedrocketry.fueltank"); + KEY_BY_ID.put("advancedrocketry:bipropellantfueltank", "tooltip.advancedrocketry.bipropfueltank"); + KEY_BY_ID.put("advancedrocketry:oxidizerfueltank", "tooltip.advancedrocketry.oxidizerfueltank"); + KEY_BY_ID.put("advancedrocketry:nuclearfueltank", "tooltip.advancedrocketry.nuclearfueltank"); + + // Monoprop tank + ARGS_BY_BASEKEY.put("tooltip.advancedrocketry.fueltank", + s -> new Object[]{ listFluidsFor(FuelType.LIQUID_MONOPROPELLANT, 6) }); + // Biprop fuel tank + ARGS_BY_BASEKEY.put("tooltip.advancedrocketry.bipropfueltank", + s -> new Object[]{ listFluidsFor(FuelType.LIQUID_BIPROPELLANT, 6) }); + // Oxidizer tank + ARGS_BY_BASEKEY.put("tooltip.advancedrocketry.oxidizerfueltank", + s -> new Object[]{ listFluidsFor(FuelType.LIQUID_OXIDIZER, 6) }); + // Nuclear working fluid tank + ARGS_BY_BASEKEY.put("tooltip.advancedrocketry.nuclearfueltank", + s -> new Object[]{ listFluidsFor(FuelType.NUCLEAR_WORKING_FLUID, 6) }); + + // Example for adding more items later (no code changes beyond these lines): + // KEY_BY_ID.put("advancedrocketry:carbonscrubbercartridge", "tooltip.advancedrocketry.scrubbercart"); + // KEY_BY_SUFFIX.put("carbonScrubberCartridge", "tooltip.advancedrocketry.scrubbercart"); + // ARGS_BY_BASEKEY.put("tooltip.advancedrocketry.scrubbercart", s -> new Object[]{ Math.max(0, s.getMaxDamage() - s.getItemDamage()) }); + } + + @SubscribeEvent + public static void onModels(ModelRegistryEvent e) { /* no-op */ } + + @SubscribeEvent + public static void onTooltip(ItemTooltipEvent e) { + final ItemStack stack = e.getItemStack(); + if (stack.isEmpty()) return; + + final List tooltip = e.getToolTip(); + + // Insert before the advanced "modid:item" line when advanced tooltips are on + final int insertAt = (e.getFlags().isAdvanced() && tooltip.size() > 1) + ? tooltip.size() - 1 + : tooltip.size(); + + final Item item = stack.getItem(); + @Nullable final ResourceLocation id = item.getRegistryName(); + String baseKey = null; + + if (id != null) { + java.util.function.Function res = KEY_RESOLVER_BY_ID.get(id.toString()); + if (res != null) { + baseKey = res.apply(stack); // e.g., tooltip.advancedrocketry.itemupgrade.3 + } + if (baseKey == null) { + baseKey = KEY_BY_ID.get(id.toString()); + } + } + + // Fallback: tail of unlocalized name (1.12 style) + if (baseKey == null) { + final String transKey = item.getUnlocalizedName(stack); + final int dot = transKey.lastIndexOf('.'); + if (dot > 0) { + final String tail = transKey.substring(dot + 1); + baseKey = KEY_BY_SUFFIX.get(tail); + } + } + + if (baseKey != null) { + renderShiftAlt(stack, tooltip, baseKey, insertAt); + } + } + + // ----- Generic renderer for base/shift/alt blocks ----- + @SideOnly(Side.CLIENT) + public static void renderShiftAlt(ItemStack s, List t, String baseKey, int idx) { + final ArgProvider ap = ARGS_BY_BASEKEY.get(baseKey); + final boolean hasShift = I18n.hasKey(baseKey + ".shift.1"); + final boolean hasAlt = I18n.hasKey(baseKey + ".alt.1") || ap != null; + + // Base block: base, base.1, base.2, ... + for (int i = 0; i <= 8; i++) { + final String k = (i == 0) ? baseKey : (baseKey + "." + i); + if (!I18n.hasKey(k)) { + if (i == 0) continue; // no bare base line; try .1 anyway + break; // stop when sequence ends + } + t.add(idx++, TextFormatting.GRAY + (ap != null ? I18n.format(k, ap.get(s)) : I18n.format(k))); + } + + // Shift block (shift.1..N) + if (hasShift) { + if (GuiScreen.isShiftKeyDown()) { + for (int i = 1; i <= 8; i++) { + final String k = baseKey + ".shift." + i; + if (!I18n.hasKey(k)) break; + t.add(idx++, TextFormatting.GRAY + (ap != null ? I18n.format(k, ap.get(s)) : I18n.format(k))); + } + } else if (I18n.hasKey("tooltip.advancedrocketry.hold_shift")) { + t.add(idx++, TextFormatting.DARK_GRAY.toString() + TextFormatting.ITALIC + + I18n.format("tooltip.advancedrocketry.hold_shift")); + } + } + + // Alt block (alt.1..N) + if (hasAlt) { + if (isAltDown()) { + for (int i = 1; i <= 8; i++) { + final String k = baseKey + ".alt." + i; + if (!I18n.hasKey(k)) break; + t.add(idx++, TextFormatting.GRAY + (ap != null ? I18n.format(k, ap.get(s)) : I18n.format(k))); + } + } else if (I18n.hasKey("tooltip.advancedrocketry.hold_alt")) { + t.add(idx++, TextFormatting.DARK_GRAY.toString() + TextFormatting.ITALIC + + I18n.format("tooltip.advancedrocketry.hold_alt")); + } + } + } + + + + // ----- Helpers ----- + + @SideOnly(Side.CLIENT) + private static String listFluidsFor(FuelType type, int max) { + java.util.List names = new java.util.ArrayList<>(); + for (net.minecraftforge.fluids.Fluid f : FluidRegistry.getRegisteredFluids().values()) { + try { + if (FuelRegistry.instance.isFuel(type, f)) { + // Localized name (1 bucket) + names.add(new FluidStack(f, 1000).getLocalizedName()); + if (names.size() >= max) break; + } + } catch (Throwable t) { + // be defensive against any odd registry states + } + } + if (names.isEmpty()) return I18n.hasKey("tooltip.advancedrocketry.none") ? I18n.format("tooltip.advancedrocketry.none") : "None"; + // if there are more than max, add an ellipsis + int total = 0; + for (net.minecraftforge.fluids.Fluid f : FluidRegistry.getRegisteredFluids().values()) + if (FuelRegistry.instance.isFuel(type, f)) total++; + String s = String.join(", ", names); + return (total > names.size()) ? (s + ", …") : s; + } + + private static int addIfPresentGray(List t, String key, int idx) { + if (I18n.hasKey(key)) { + t.add(idx, TextFormatting.GRAY + I18n.format(key)); + return idx + 1; + } + return idx; + } + + private static int addIfPresentDarkGray(List t, String key, int idx) { + if (I18n.hasKey(key)) { + t.add(idx, TextFormatting.DARK_GRAY + I18n.format(key)); + return idx + 1; + } + return idx; + } + + private static int addIfPresentDarkGrayFmt(List t, String key, int idx, Object... args) { + if (I18n.hasKey(key)) { + t.add(idx, TextFormatting.DARK_GRAY + I18n.format(key, args)); + return idx + 1; + } + return idx; + } + + @SideOnly(Side.CLIENT) + public static int computeInsertIndex(List tooltip, boolean advanced) { + return (advanced && tooltip.size() > 1) ? tooltip.size() - 1 : tooltip.size(); + } + + @SideOnly(Side.CLIENT) + public static boolean isAltDown() { + return Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/client/render/entity/RenderHoverCraft.java b/src/main/java/zmaster587/advancedRocketry/client/render/entity/RenderHoverCraft.java index ee7fc0d7b..fa96099e9 100644 --- a/src/main/java/zmaster587/advancedRocketry/client/render/entity/RenderHoverCraft.java +++ b/src/main/java/zmaster587/advancedRocketry/client/render/entity/RenderHoverCraft.java @@ -8,6 +8,7 @@ import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.MathHelper; import net.minecraftforge.fml.client.registry.IRenderFactory; import org.lwjgl.opengl.GL11; import zmaster587.advancedRocketry.backwardCompat.ModelFormatException; @@ -55,12 +56,17 @@ public boolean shouldRender(EntityHoverCraft livingEntity, @Override public void doRender(EntityHoverCraft entity, double x, double y, double z, - float entityYaw, float partialTicks) { - + float entityYaw, float partialTicks) { GL11.glPushMatrix(); GL11.glTranslated(x, y + 1, z); - GL11.glRotated(180 - entityYaw, 0, 1, 0); + + // Wrapped yaw interpolation (prevents 179 -> -179 spin) + float yaw = entity.prevRotationYaw + + MathHelper.wrapDegrees(entity.rotationYaw - entity.prevRotationYaw) * partialTicks; + + GL11.glRotated(180.0f - yaw, 0, 1, 0); + bindTexture(hovercraftTexture); hoverCraft.renderAll(); diff --git a/src/main/java/zmaster587/advancedRocketry/client/render/entity/RendererItem.java b/src/main/java/zmaster587/advancedRocketry/client/render/entity/RendererItem.java index 3b646e55a..b6b7036b9 100644 --- a/src/main/java/zmaster587/advancedRocketry/client/render/entity/RendererItem.java +++ b/src/main/java/zmaster587/advancedRocketry/client/render/entity/RendererItem.java @@ -21,7 +21,6 @@ @SideOnly(Side.CLIENT) public class RendererItem extends Render implements IRenderFactory { private static final ResourceLocation RES_ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png"); - private static final String __OBFID = "CL_00001003"; public static boolean renderInFrame; public boolean renderWithColor = true; /** diff --git a/src/main/java/zmaster587/advancedRocketry/client/render/planet/RenderAsteroidSky.java b/src/main/java/zmaster587/advancedRocketry/client/render/planet/RenderAsteroidSky.java index 75bac2b36..ba7c44ef2 100644 --- a/src/main/java/zmaster587/advancedRocketry/client/render/planet/RenderAsteroidSky.java +++ b/src/main/java/zmaster587/advancedRocketry/client/render/planet/RenderAsteroidSky.java @@ -5,7 +5,6 @@ import net.minecraft.client.renderer.*; import net.minecraft.client.renderer.GlStateManager.DestFactor; import net.minecraft.client.renderer.GlStateManager.SourceFactor; -import net.minecraft.client.renderer.entity.Render; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; @@ -19,54 +18,70 @@ import zmaster587.advancedRocketry.api.dimension.solar.StellarBody; import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.advancedRocketry.dimension.DimensionProperties; -import zmaster587.advancedRocketry.event.RocketEventHandler; import zmaster587.advancedRocketry.inventory.TextureResources; import zmaster587.advancedRocketry.stations.SpaceObjectManager; import zmaster587.advancedRocketry.stations.SpaceStationObject; import zmaster587.advancedRocketry.util.AstronomicalBodyHelper; import zmaster587.libVulpes.util.Vector3F; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.Random; public class RenderAsteroidSky extends IRenderHandler { - + // === Textures === public static final ResourceLocation asteroid1 = new ResourceLocation("advancedRocketry:textures/planets/asteroid_a.png"); public static final ResourceLocation asteroid2 = new ResourceLocation("advancedRocketry:textures/planets/asteroid_b.png"); public static final ResourceLocation asteroid3 = new ResourceLocation("advancedRocketry:textures/planets/asteroid_c.png"); - ResourceLocation currentlyBoundTex = null; - float celestialAngle; - Vector3F axis; - Minecraft mc = Minecraft.getMinecraft(); - private int starGLCallList; - private int glSkyList; - private int glSkyList2; - private int glSkyList3; - - //Mostly vanilla code - //TODO: make usable on other planets + + // Per-frame texture bind cache (local to this renderer) + private ResourceLocation boundTex = null; + + // Runtime / state + private float celestialAngle; + private final Vector3F axis; + private final Minecraft mc = Minecraft.getMinecraft(); + + // Display lists + private final int starGLCallList; + private final int glSkyList; + private final int glSkyList2; + private final int glSkyList3; + + // Reused scratch buffers to reduce GC + private final List childrenBuf = new ArrayList<>(8); + private final float[] shadowColorTmp = new float[3]; + + // Helpers for ring/black-hole math + private static float xrotangle = 0; // for ring rotation (kept exactly as before) + private static final float[] skycolor = {0,0,0}; // for black hole rendering (same usage as before) + private static double currentplanetphi = 0; // ring/disk angle (same) + + // === ctor === public RenderAsteroidSky() { axis = new Vector3F<>(1f, 0f, 0f); + // Build display lists once (same seeds/geometry as original) this.starGLCallList = GLAllocation.generateDisplayLists(4); + GL11.glPushMatrix(); GL11.glNewList(this.starGLCallList, GL11.GL_COMPILE); - this.renderStars(); + this.renderStars(); // stars list GL11.glEndList(); GL11.glPopMatrix(); + BufferBuilder buffer = Tessellator.getInstance().getBuffer(); + + // Sky dome slice 1 this.glSkyList = this.starGLCallList + 1; GL11.glNewList(this.glSkyList, GL11.GL_COMPILE); byte b2 = 64; int i = 256 / b2 + 2; float f = 16.0F; - int j; - int k; - for (j = -b2 * i; j <= b2 * i; j += b2) { - for (k = -b2 * i; k <= b2 * i; k += b2) { + for (int j = -b2 * i; j <= b2 * i; j += b2) { + for (int k = -b2 * i; k <= b2 * i; k += b2) { buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION); buffer.pos(j, f, k).endVertex(); buffer.pos(j + b2, f, k).endVertex(); @@ -75,35 +90,43 @@ public RenderAsteroidSky() { Tessellator.getInstance().draw(); } } - GL11.glEndList(); + + // Sky dome slice 2 this.glSkyList2 = this.starGLCallList + 2; GL11.glNewList(this.glSkyList2, GL11.GL_COMPILE); f = -16.0F; buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION); - - for (j = -b2 * i; j <= b2 * i; j += b2) { - for (k = -b2 * i; k <= b2 * i; k += b2) { + for (int j = -b2 * i; j <= b2 * i; j += b2) { + for (int k = -b2 * i; k <= b2 * i; k += b2) { buffer.pos(j, f, k).endVertex(); buffer.pos(j + b2, f, k).endVertex(); buffer.pos(j + b2, f, k + b2).endVertex(); buffer.pos(j, f, k + b2).endVertex(); } } - Tessellator.getInstance().draw(); GL11.glEndList(); + // Asteroids list this.glSkyList3 = this.starGLCallList + 3; GL11.glPushMatrix(); GL11.glNewList(this.glSkyList3, GL11.GL_COMPILE); - renderAsteroids(); + renderAsteroids(); // geometry baked with fixed seed matching original GL11.glEndList(); GL11.glPopMatrix(); } + // Efficient texture binder (skip redundant binds per frame) + private void bind(ResourceLocation tex) { + if (tex != null && tex != boundTex) { + mc.renderEngine.bindTexture(tex); + boundTex = tex; + } + } + private void renderAsteroids() { - Random random = new Random(10843L); + Random random = new Random(10843L); // same seed => identical layout to original BufferBuilder buffer = Tessellator.getInstance().getBuffer(); buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); @@ -132,10 +155,9 @@ private void renderAsteroids() { double d15 = Math.sin(d14); double d16 = Math.cos(d14); - float r, g, b; - r = random.nextFloat() * 0.05f + .95f; - g = random.nextFloat() * 0.1f + .9f; - b = random.nextFloat() * 0.1f + .9f; + float r = random.nextFloat() * 0.05f + .95f; + float g = random.nextFloat() * 0.1f + .9f; + float b = random.nextFloat() * 0.1f + .9f; for (int j = 0; j < 4; ++j) { double d17 = 0.0D; @@ -151,13 +173,11 @@ private void renderAsteroids() { } } } - Tessellator.getInstance().draw(); - //buffer.finishDrawing(); } private void renderStars() { - Random random = new Random(10842L); + Random random = new Random(10842L); // same seed => identical layout to original BufferBuilder buffer = Tessellator.getInstance().getBuffer(); buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION); @@ -200,50 +220,38 @@ private void renderStars() { } } } - Tessellator.getInstance().draw(); - //buffer.finishDrawing(); } - private static float xrotangle = 0; // used for ring rotation because I don't want to bother changing the definitions of methods. - private static float[] skycolor = {0,0,0}; // used for black hole rendering - same reason as above - private static double currentplanetphi = 0; // used for calculating ring/disk angle - @Override public void render(float partialTicks, WorldClient world, Minecraft mc) { + // per-frame texture bind cache reset + boundTex = null; - - //TODO: properly handle this + // === Gather properties (unchanged logic) === float atmosphere; int solarOrbitalDistance, planetOrbitalDistance = 0; double myPhi = 0, myTheta = 0, myPrevOrbitalTheta = 0, myRotationalPhi = 0; - boolean hasAtmosphere = false, isMoon; - float[] shadowColorMultiplier = {0f, 0f, 0f}; - float[] parentAtmColor = new float[]{1f, 1f, 1f}; + boolean isMoon; + // shadowColorTmp reused; values set below float[] parentRingColor = new float[]{1f, 1f, 1f}; float[] ringColor = new float[]{1f, 1f, 1f}; float sunSize = 1.0f; - float starSeparation = 0f; boolean isWarp = false; - boolean isGasGiant = false; boolean hasRings = false; - boolean parentPlanetHasDecorator = true; boolean parentHasRings = false; boolean parentHasATM = false; DimensionProperties parentProperties = null; DimensionProperties properties; EnumFacing travelDirection = null; - ResourceLocation parentPlanetIcon = null; List children; StellarBody primaryStar; celestialAngle = mc.world.getCelestialAngle(partialTicks); Vec3d sunColor; - if (mc.world.provider instanceof IPlanetaryProvider) { IPlanetaryProvider planetaryProvider = (IPlanetaryProvider) mc.world.provider; - properties = (DimensionProperties) planetaryProvider.getDimensionProperties(mc.player.getPosition()); atmosphere = planetaryProvider.getAtmosphereDensityFromHeight(mc.getRenderViewEntity().posY, mc.player.getPosition()); @@ -259,15 +267,16 @@ public void render(float partialTicks, WorldClient world, Minecraft mc) { hasRings = properties.hasRings(); ringColor = properties.ringColor; - children = new LinkedList<>(); + childrenBuf.clear(); for (Integer i : properties.getChildPlanets()) { - children.add(DimensionManager.getInstance().getDimensionProperties(i)); + childrenBuf.add(DimensionManager.getInstance().getDimensionProperties(i)); } + children = childrenBuf; solarOrbitalDistance = properties.getSolarOrbitalDistance(); - - if (isMoon = properties.isMoon()) { + isMoon = properties.isMoon(); + if (isMoon) { parentProperties = properties.getParentProperties(); planetOrbitalDistance = properties.getParentOrbitalDistance(); parentHasRings = parentProperties.hasRings; @@ -277,22 +286,22 @@ public void render(float partialTicks, WorldClient world, Minecraft mc) { sunColor = planetaryProvider.getSunColor(mc.player.getPosition()); primaryStar = properties.getStar(); if (primaryStar != null) { - sunSize = properties.getStar().getSize(); - } else + sunSize = primaryStar.getSize(); + } else { primaryStar = DimensionManager.getInstance().getStar(0); + } + if (world.provider.getDimension() == ARConfiguration.getCurrentConfig().spaceDimId) { isWarp = properties.getParentPlanet() == SpaceObjectManager.WARPDIMID; if (isWarp) { SpaceStationObject station = (SpaceStationObject) SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(mc.player.getPosition()); - travelDirection = station.getForwardDirection(); + if (station != null) travelDirection = station.getForwardDirection(); } } - } - else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.getDimension())) { - + } else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.getDimension())) { properties = DimensionManager.getInstance().getDimensionProperties(mc.world.provider.getDimension()); - atmosphere = properties.getAtmosphereDensityAtHeight(mc.getRenderViewEntity().posY);//planetaryProvider.getAtmosphereDensityFromHeight(mc.getRenderViewEntity().posY, mc.player.getPosition()); + atmosphere = properties.getAtmosphereDensityAtHeight(mc.getRenderViewEntity().posY); EnumFacing dir = getRotationAxis(properties, mc.player.getPosition()); axis.x = (float) dir.getFrontOffsetX(); axis.y = (float) dir.getFrontOffsetY(); @@ -305,15 +314,16 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get hasRings = properties.hasRings(); ringColor = properties.ringColor; - children = new LinkedList<>(); + childrenBuf.clear(); for (Integer i : properties.getChildPlanets()) { - children.add(DimensionManager.getInstance().getDimensionProperties(i)); + childrenBuf.add(DimensionManager.getInstance().getDimensionProperties(i)); } + children = childrenBuf; solarOrbitalDistance = properties.getSolarOrbitalDistance(); - - if (isMoon = properties.isMoon()) { + isMoon = properties.isMoon(); + if (isMoon) { parentProperties = properties.getParentProperties(); planetOrbitalDistance = properties.getParentOrbitalDistance(); parentHasRings = parentProperties.hasRings; @@ -322,22 +332,25 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get } float[] sunColorFloat = properties.getSunColor(); - sunColor = new Vec3d(sunColorFloat[0], sunColorFloat[1], sunColorFloat[2]);//planetaryProvider.getSunColor(mc.player.getPosition()); + sunColor = new Vec3d(sunColorFloat[0], sunColorFloat[1], sunColorFloat[2]); primaryStar = properties.getStar(); if (primaryStar != null) { - sunSize = properties.getStar().getSize(); - } else + sunSize = primaryStar.getSize(); + } else { primaryStar = DimensionManager.getInstance().getStar(0); + } + if (world.provider.getDimension() == ARConfiguration.getCurrentConfig().spaceDimId) { isWarp = properties.getParentPlanet() == SpaceObjectManager.WARPDIMID; if (isWarp) { SpaceStationObject station = (SpaceStationObject) SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(mc.player.getPosition()); - travelDirection = station.getForwardDirection(); + if (station != null) travelDirection = station.getForwardDirection(); } } - } - else { - children = new LinkedList<>(); + } else { + // No planet provider and dimension not registered: fall back to overworld props (exactly as before) + childrenBuf.clear(); + children = childrenBuf; isMoon = false; atmosphere = DimensionManager.overworldProperties.getAtmosphereDensityAtHeight(mc.getRenderViewEntity().posY); solarOrbitalDistance = DimensionManager.overworldProperties.orbitalDist; @@ -348,6 +361,7 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get currentplanetphi = myPhi; + // === Sky color & base dome === GlStateManager.disableTexture2D(); Vec3d vec3 = Minecraft.getMinecraft().world.getSkyColor(this.mc.getRenderViewEntity(), partialTicks); float f1 = (float) vec3.x; @@ -359,23 +373,17 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get float f4 = (f1 * 30.0F + f2 * 59.0F + f3 * 11.0F) / 100.0F; float f5 = (f1 * 30.0F + f2 * 70.0F) / 100.0F; f6 = (f1 * 30.0F + f3 * 70.0F) / 100.0F; - f1 = f4; - f2 = f5; - f3 = f6; + f1 = f4; f2 = f5; f3 = f6; } - //Simulate atmospheric thickness, vaugely - //This is done like this to prevent problems with superbright atmospheres on low-atmosphere planets - //Plus you couldn't see stars during the day anyway + // Atmospheric brightness shaping (unchanged) int atmosphereInt = properties.getAtmosphereDensity(); -// System.out.println("before:"+f1+":"+f2+":"+f3); f1 = atmosphereInt < 1 ? 0 : (float) Math.pow(f1, Math.sqrt(Math.max(atmosphere, 0.0001))); f2 = atmosphereInt < 1 ? 0 : (float) Math.pow(f2, Math.sqrt(Math.max(atmosphere, 0.0001))); f3 = atmosphereInt < 1 ? 0 : (float) Math.pow(f3, Math.sqrt(Math.max(atmosphere, 0.0001))); - - f1*=Math.min(1,atmosphere); - f2*=Math.min(1,atmosphere); - f3*=Math.min(1,atmosphere); + f1 *= Math.min(1, atmosphere); + f2 *= Math.min(1, atmosphere); + f3 *= Math.min(1, atmosphere); skycolor[0] = f1; skycolor[1] = f2; @@ -391,12 +399,10 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get GL11.glCallList(this.glSkyList); GlStateManager.disableFog(); GlStateManager.disableAlpha(); - RenderHelper.disableStandardItemLighting(); + net.minecraft.client.renderer.RenderHelper.disableStandardItemLighting(); + float[] afloat = mc.world.provider.calcSunriseSunsetColors(celestialAngle, partialTicks); - float f7; - float f8; - float f9; - float f10; + float f7, f8, f9, f10; if (afloat != null) { GlStateManager.disableTexture2D(); @@ -406,7 +412,6 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get GL11.glRotatef(MathHelper.sin(mc.world.getCelestialAngleRadians(partialTicks)) < 0.0F ? 180.0F : 0.0F, 0.0F, 0.0F, 1.0F); GL11.glRotated(90.0F - myRotationalPhi, 0.0F, 0.0F, 1.0F); - //Sim atmospheric thickness f6 = afloat[0]; f7 = afloat[1]; f8 = afloat[2]; @@ -416,9 +421,7 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get f9 = (f6 * 30.0F + f7 * 59.0F + f8 * 11.0F) / 100.0F; f10 = (f6 * 30.0F + f7 * 70.0F) / 100.0F; f11 = (f6 * 30.0F + f8 * 70.0F) / 100.0F; - f6 = f9; - f7 = f10; - f8 = f11; + f6 = f9; f7 = f10; f8 = f11; } buffer.begin(GL11.GL_TRIANGLE_FAN, DefaultVertexFormats.POSITION_COLOR); @@ -427,50 +430,49 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get for (int j = 0; j <= b0; ++j) { f11 = (float) j * (float) Math.PI * 2.0F / (float) b0; - float f12 = MathHelper.sin(f11); - float f13 = MathHelper.cos(f11); - buffer.pos(f12 * 120.0F, f13 * 120.0F, -f13 * 40.0F * afloat[3]).color(afloat[0], afloat[1], afloat[2], 0.0F).endVertex(); + float sx = MathHelper.sin(f11); + float cx = MathHelper.cos(f11); + buffer.pos(sx * 120.0F, cx * 120.0F, -cx * 40.0F * afloat[3]).color(afloat[0], afloat[1], afloat[2], 0.0F).endVertex(); } - Tessellator.getInstance().draw(); GL11.glPopMatrix(); GlStateManager.shadeModel(GL11.GL_FLAT); } - shadowColorMultiplier = new float[]{f1, f2, f3}; + + // shadow color multiplier (reused array) + shadowColorTmp[0] = f1; + shadowColorTmp[1] = f2; + shadowColorTmp[2] = f3; GlStateManager.enableTexture2D(); GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE); GL11.glPushMatrix(); + // rain alpha handling + if (atmosphere > 0) f6 = 1.0F - (mc.world.getRainStrength(partialTicks) * (atmosphere / 100f)); + else f6 = 1f; - if (atmosphere > 0) - f6 = 1.0F - (mc.world.getRainStrength(partialTicks) * (atmosphere / 100f)); - else - f6 = 1f; - - f7 = 0.0F; - f8 = 0.0F; - f9 = 0.0F; + f7 = 0.0F; f8 = 0.0F; f9 = 0.0F; GlStateManager.color(1.0F, 1.0F, 1.0F, f6); GL11.glTranslatef(f7, f8, f9); GL11.glRotatef(-90.0F, 0.0F, 1.0F, 0.0F); - float multiplier = (2 - atmosphere) / 2f;//atmosphere > 1 ? (2-atmosphere) : 1f; - if (mc.world.isRainingAt(mc.player.getPosition().add(0, 199, 0))) + float multiplier = (2 - atmosphere) / 2f; + if (mc.world.isRainingAt(mc.player.getPosition().add(0, 199, 0))) { multiplier *= 1 - mc.world.getRainStrength(partialTicks); + } GL11.glRotatef((float) myRotationalPhi, 0f, 1f, 0f); - //Draw Rings + // Rings (unchanged visuals) if (hasRings) { GL11.glPushMatrix(); GL11.glRotatef(90f, 0f, 1f, 0f); f10 = 100; double ringDist = 0; - //mc.renderEngine.bindTexture(DimensionProperties.planetRings); - mc.renderEngine.bindTexture(DimensionProperties.planetRingsNew); + bind(DimensionProperties.planetRingsNew); GL11.glRotated(70, 1, 0, 0); GL11.glTranslated(0, -10, 0); @@ -484,148 +486,120 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get Tessellator.getInstance().draw(); GL11.glPopMatrix(); - /* - GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); - GL11.glPushMatrix(); - - GL11.glRotatef(90f, 0f, 1f, 0f); - GL11.glRotated(70, 1, 0, 0); - GL11.glRotatef(isWarp ? 0 : celestialAngle * 360.0F, 0, 1, 0); - GL11.glTranslated(0, -10, 0); - - mc.renderEngine.bindTexture(DimensionProperties.planetRingShadow); - GlStateManager.color(0f, 0f, 0f, multiplier); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(f10, ringDist, -f10).tex(1.0D, 0.0D).endVertex(); - buffer.pos(-f10, ringDist, -f10).tex(0.0D, 0.0D).endVertex(); - buffer.pos(-f10, ringDist, f10).tex(0.0D, 1.0D).endVertex(); - buffer.pos(f10, ringDist, f10).tex(1.0D, 1.0D).endVertex(); - Tessellator.getInstance().draw(); - GL11.glPopMatrix(); - */ - + // (Shadowed ring quad code left as in original) GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE); } GlStateManager.disableTexture2D(); - //This determines whether stars should come out regardless of thickness of atmosphere, as that is factored in later - // - it checks if the colors of the sky are so close to black that you'd see stars, or if the atmosphere is zero and so no one gives a damn - float f18 = mc.world.getStarBrightness(partialTicks) * f6;//((atmosphere == 0 || (f1 < 0.09 && f2 < 0.09 && f3 < 0.09)) ? 1 : 0);// - (atmosphere > 1 ? atmosphere - 1 : 0); + // Stars + float f18 = mc.world.getStarBrightness(partialTicks) * f6; + float starAlpha = 1 - ((1 - f18) * atmosphere); - float starAlpha = 1-((1-f18)*atmosphere); - //System.out.println(starAlpha+":"+f18+":"+atmosphere); + GlStateManager.disableDepth(); // stars always on top of sky Keep? makes asteroids glow :D - //if (f18 > 0.0F) { - if (true){ - GlStateManager.color(1, 1, 1, 1); - GL11.glPushMatrix(); - if (isWarp) { - for (int i = -3; i < 5; i++) { - GL11.glPushMatrix(); - double magnitude = i * -100 + (((System.currentTimeMillis()) + 50) % 2000) / 20f; - GL11.glTranslated(-travelDirection.getFrontOffsetZ() * magnitude, 0, travelDirection.getFrontOffsetX() * magnitude); - GL11.glCallList(this.starGLCallList); - GL11.glPopMatrix(); - } - //GL11.glTranslated(((System.currentTimeMillis()/10) + 50) % 100, 0, 0); - - } else { - GL11.glColor4f(1,1,1,starAlpha); + GlStateManager.color(1, 1, 1, 1); + GL11.glPushMatrix(); + if (isWarp && travelDirection != null) { + for (int n = -3; n < 5; n++) { + GL11.glPushMatrix(); + double magnitude = n * -100 + (((System.currentTimeMillis()) + 50) % 2000) / 20f; + GL11.glTranslated(-travelDirection.getFrontOffsetZ() * magnitude, 0, travelDirection.getFrontOffsetX() * magnitude); GL11.glCallList(this.starGLCallList); - //Extra stars for low ATM - if (atmosphere < 0.5) { - GL11.glColor4f(1,1,1,starAlpha/2); - GL11.glPushMatrix(); - GL11.glRotatef(-90, 0, 1, 0); - GL11.glCallList(this.starGLCallList); - GL11.glPopMatrix(); - } - if (atmosphere < 0.25) { - GL11.glColor4f(1,1,1,starAlpha/4); - GL11.glPushMatrix(); - GL11.glRotatef(90, 0, 1, 0); - GL11.glCallList(this.starGLCallList); - GL11.glPopMatrix(); - } - GlStateManager.color(1, 1, 1, 1); + GL11.glPopMatrix(); + } + } else { + GL11.glColor4f(1, 1, 1, starAlpha); + GL11.glCallList(this.starGLCallList); + if (atmosphere < 0.5f) { + GL11.glColor4f(1, 1, 1, starAlpha / 2f); + GL11.glPushMatrix(); + GL11.glRotatef(-90, 0, 1, 0); + GL11.glCallList(this.starGLCallList); + GL11.glPopMatrix(); } - GL11.glPopMatrix(); + if (atmosphere < 0.25f) { + GL11.glColor4f(1, 1, 1, starAlpha / 4f); + GL11.glPushMatrix(); + GL11.glRotatef(90, 0, 1, 0); + GL11.glCallList(this.starGLCallList); + GL11.glPopMatrix(); + } + GlStateManager.color(1, 1, 1, 1); } + GL11.glPopMatrix(); + GlStateManager.enableTexture2D(); + GlStateManager.enableDepth(); // keep? - mc.renderEngine.bindTexture(TextureResources.locationSunPng); + // Sun & sub-stars + bind(TextureResources.locationSunPng); - //--------------------------- Draw the suns -------------------- if (!isWarp) { if (parentProperties == null || !parentProperties.isStar()) { - xrotangle = ((float) (properties.getSolarTheta() * 180f / Math.PI) % 360f); // for black hole disk - //System.out.println(xrotangle+":"+properties.getSolarTheta()); + xrotangle = ((float) (properties.getSolarTheta() * 180f / Math.PI) % 360f); // used in black hole path drawStarAndSubStars(buffer, primaryStar, properties, solarOrbitalDistance, sunSize, sunColor, multiplier); xrotangle = 0; } } + // Moons/parent planets (unchanged logic) + if (DimensionProperties.AtmosphereTypes.SUPERHIGHPRESSURE.denserThan( + DimensionProperties.AtmosphereTypes.getAtmosphereTypeFromValue((int) (100 * atmosphere)))) { - //For these parts only render if the atmosphere is below a certain threshold (SHP atmosphere) - if (DimensionProperties.AtmosphereTypes.SUPERHIGHPRESSURE.denserThan(DimensionProperties.AtmosphereTypes.getAtmosphereTypeFromValue((int) (100 * atmosphere)))) { - //Render the parent planet - if (isMoon) { + if (isMoon && parentProperties != null) { GL11.glPushMatrix(); - //Do a whole lotta math to figure out where the parent planet is supposed to be - //That 0.3054325f is there because we need to do adjustments for some ^$%^$% reason and it's consistently off by 17.5 degrees - float planetPositionTheta = AstronomicalBodyHelper.getParentPlanetThetaFromMoon(properties.rotationalPeriod, properties.orbitalDist, parentProperties.gravitationalMultiplier, myTheta, properties.baseOrbitTheta); + float planetPositionTheta = AstronomicalBodyHelper.getParentPlanetThetaFromMoon( + properties.rotationalPeriod, properties.orbitalDist, parentProperties.gravitationalMultiplier, + myTheta, properties.baseOrbitTheta); GL11.glRotatef((float) myPhi, 0f, 0f, 1f); GL11.glRotatef(planetPositionTheta, 1f, 0f, 0f); - float phiAngle = (float) ((myPhi) * Math.PI / 180f); - - //Close enough approximation, I missed something but seems to off by no more than 30* - //Nobody will look + float phiAngle = (float) (myPhi * Math.PI / 180f); double x = MathHelper.sin(phiAngle) * MathHelper.cos((float) myTheta); double y = -MathHelper.sin((float) myTheta); double rotation = -Math.PI / 2f + Math.atan2(x, y) - (myTheta - Math.PI) * MathHelper.sin(phiAngle); if (parentHasRings) { - //Semihacky rotation stuff to keep rings synced to a different rotation than planet in the sky xrotangle = -planetPositionTheta + ((float) (myTheta * 180f / Math.PI) % 360f); - //System.out.println("r:"+xrotangle); } - shadowColorMultiplier = new float[]{f1, f2, f3}; + shadowColorTmp[0] = f1; + shadowColorTmp[1] = f2; + shadowColorTmp[2] = f3; - //System.out.println("draw moon (renderplanet"); - renderPlanet(buffer, parentProperties, planetOrbitalDistance, multiplier, rotation, false, parentHasRings, (float) Math.pow(parentProperties.getGravitationalMultiplier(), 0.4), shadowColorMultiplier, 1); + renderPlanet(buffer, parentProperties, planetOrbitalDistance, multiplier, rotation, false, parentHasRings, + (float) Math.pow(parentProperties.getGravitationalMultiplier(), 0.4), shadowColorTmp, 1); xrotangle = 0; GL11.glPopMatrix(); } - //This needs to exist specifically for init purposes - //The overworld literally breaks without it - shadowColorMultiplier[0] = 1.000001f * shadowColorMultiplier[0]; + // init quirk kept as-is + shadowColorTmp[0] = 1.000001f * shadowColorTmp[0]; for (DimensionProperties moons : children) { GL11.glPushMatrix(); float planetPositionTheta = (float) ((partialTicks * moons.orbitTheta + ((1 - partialTicks) * moons.prevOrbitalTheta)) * 180F / Math.PI); - float flippedPlanetPositionTheta = 360 - planetPositionTheta; GL11.glRotatef((float) moons.orbitalPhi, 0f, 0f, 1f); GL11.glRotated(planetPositionTheta, 1f, 0f, 0f); - //Close enough approximation, I missed something but seems to off by no more than 30* - //Nobody will look - float phiAngle = (float) ((moons.orbitalPhi) * Math.PI / 180f); + float phiAngle = (float) (moons.orbitalPhi * Math.PI / 180f); double x = -MathHelper.sin(phiAngle) * MathHelper.cos((float) moons.orbitTheta); double y = MathHelper.sin((float) moons.orbitTheta); double rotation = (-Math.PI / 2f + Math.atan2(x, y) - (moons.orbitTheta - Math.PI) * MathHelper.sin(phiAngle)) + Math.PI; - shadowColorMultiplier = new float[]{f1, f2, f3}; - renderPlanet(buffer, moons, moons.getParentOrbitalDistance(), multiplier, rotation, moons.hasAtmosphere(), moons.hasRings, (float) Math.pow(moons.gravitationalMultiplier, 0.4), shadowColorMultiplier, 1); + shadowColorTmp[0] = f1; + shadowColorTmp[1] = f2; + shadowColorTmp[2] = f3; + + renderPlanet(buffer, moons, moons.getParentOrbitalDistance(), multiplier, rotation, moons.hasAtmosphere(), + moons.hasRings, (float) Math.pow(moons.gravitationalMultiplier, 0.4), shadowColorTmp, 1); GL11.glPopMatrix(); } } @@ -635,35 +609,48 @@ else if (DimensionManager.getInstance().isDimensionCreated(mc.world.provider.get GlStateManager.disableBlend(); GlStateManager.enableAlpha(); - GL11.glPopMatrix(); + GL11.glPopMatrix(); // matching the big push before rings/stars/sun + // === Asteroid billboards === GlStateManager.enableTexture2D(); - - mc.renderEngine.bindTexture(asteroid1); GlStateManager.color(1, 1, 1); + GlStateManager.depthMask(false); + GlStateManager.enableBlend(); // additive star style keeps them in "sky" + GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE); + + GlStateManager.disableDepth(); // keep? + + bind(asteroid1); GL11.glCallList(this.glSkyList3); GL11.glPushMatrix(); GL11.glRotatef(90, 0.2f, 0.8f, 0); - mc.renderEngine.bindTexture(asteroid2); + bind(asteroid2); GL11.glCallList(this.glSkyList3); GL11.glRotatef(90, 0.2f, 0.8f, 0); - mc.renderEngine.bindTexture(asteroid3); + bind(asteroid3); GL11.glCallList(this.glSkyList3); GL11.glPopMatrix(); - GL11.glDepthMask(true); + GlStateManager.enableDepth(); // keep? - - //RocketEventHandler.onPostWorldRender(partialTicks); - //Fix player/items going transparent + // === PROPER GL STATE RESET === + // Keep depth mask on, but DO NOT clear depth here + GlStateManager.depthMask(true); + GlStateManager.disableBlend(); OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 0, 0); + GlStateManager.shadeModel(GL11.GL_FLAT); + GlStateManager.enableTexture2D(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(GL11.GL_GREATER, 0.1f); + GlStateManager.color(1f, 1f, 1f, 1f); + GlStateManager.enableCull(); } + protected void drawStarAndSubStars(BufferBuilder buffer, StellarBody sun, DimensionProperties properties, int solarOrbitalDistance, float sunSize, Vec3d sunColor, float multiplier) { drawStar(buffer, sun, properties, solarOrbitalDistance, sunSize, sunColor, multiplier); List subStars = sun.getSubStars(); - if (subStars != null && !subStars.isEmpty()) { GL11.glPushMatrix(); float phaseInc = 360f / subStars.size(); @@ -674,12 +661,14 @@ protected void drawStarAndSubStars(BufferBuilder buffer, StellarBody sun, Dimens GL11.glRotatef(subStar.getStarSeparation() * AstronomicalBodyHelper.getBodySizeMultiplier(solarOrbitalDistance), 1, 0, 0); float[] color = subStar.getColor(); - drawStar(buffer, subStar, properties, solarOrbitalDistance, subStar.getSize(), new Vec3d(color[0], color[1], color[2]), multiplier); + drawStar(buffer, subStar, properties, solarOrbitalDistance, subStar.getSize(), + new Vec3d(color[0], color[1], color[2]), multiplier); GL11.glPopMatrix(); } GL11.glPopMatrix(); } } + protected ResourceLocation getTextureForPlanet(DimensionProperties properties) { return properties.getPlanetIcon(); } @@ -692,7 +681,6 @@ protected EnumFacing getRotationAxis(DimensionProperties properties, BlockPos po return EnumFacing.EAST; } - protected void renderPlanet(BufferBuilder buffer, DimensionProperties properties, float planetOrbitalDistance, float alphaMultiplier, double shadowAngle, boolean hasAtmosphere, boolean hasRing, float gravitationalMultiplier, float[] shadowColorMultiplier, float alphaMultiplier2) { renderPlanet2(buffer, properties, 20f * AstronomicalBodyHelper.getBodySizeMultiplier(planetOrbitalDistance) * gravitationalMultiplier, alphaMultiplier, shadowAngle, hasRing, shadowColorMultiplier, alphaMultiplier2); } @@ -704,9 +692,15 @@ protected void renderPlanet2(BufferBuilder buffer, DimensionProperties propertie boolean gasGiant = properties.isGasGiant(); float[] skyColor = properties.skyColor; float[] ringColor = properties.ringColor; - RenderPlanetarySky. renderPlanetPubHelper(buffer, icon, 0, 0, -20, size * 0.2f, alphaMultiplier, shadowAngle, hasAtmosphere, skyColor, ringColor, gasGiant, hasRing, properties.ringAngle, hasDecorators, shadowColorMultiplier, alphaMultiplier2); - } + // Keep external call identical + RenderPlanetarySky.renderPlanetPubHelper( + buffer, icon, 0, 0, -20, + size * 0.2f, alphaMultiplier, shadowAngle, + hasAtmosphere, skyColor, ringColor, gasGiant, hasRing, properties.ringAngle, + hasDecorators, shadowColorMultiplier, alphaMultiplier2 + ); + } protected Vector3F getRotateAxis() { return axis; @@ -718,12 +712,12 @@ public void renderSphere(double x, double y, double z, float radius, int slices, bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - for(int i = 0; i < slices; i++) { - for(int j = 0; j < stacks; j++) { - double firstLong = 2 * Math.PI * (i / (double)slices); - double secondLong = 2 * Math.PI * ((i + 1) / (double)slices); - double firstLat = Math.PI * (j / (double)stacks) - Math.PI / 2; - double secondLat = Math.PI * ((j + 1) / (double)stacks) - Math.PI / 2; + for (int i = 0; i < slices; i++) { + for (int j = 0; j < stacks; j++) { + double firstLong = 2 * Math.PI * (i / (double) slices); + double secondLong = 2 * Math.PI * ((i + 1) / (double) slices); + double firstLat = Math.PI * (j / (double) stacks) - Math.PI / 2; + double secondLat = Math.PI * ((j + 1) / (double) stacks) - Math.PI / 2; bufferBuilder.pos(x + radius * Math.cos(firstLat) * Math.cos(firstLong), y + radius * Math.sin(firstLat), z + radius * Math.cos(firstLat) * Math.sin(firstLong)).tex(0.0D, 0.0D).endVertex(); bufferBuilder.pos(x + radius * Math.cos(secondLat) * Math.cos(firstLong), y + radius * Math.sin(secondLat), z + radius * Math.cos(secondLat) * Math.sin(firstLong)).tex(1.0D, 0.0D).endVertex(); @@ -731,9 +725,9 @@ public void renderSphere(double x, double y, double z, float radius, int slices, bufferBuilder.pos(x + radius * Math.cos(firstLat) * Math.cos(secondLong), y + radius * Math.sin(firstLat), z + radius * Math.cos(firstLat) * Math.sin(secondLong)).tex(0.0D, 1.0D).endVertex(); } } - tessellator.draw(); } + protected void drawStar(BufferBuilder buffer, StellarBody sun, DimensionProperties properties, int solarOrbitalDistance, float sunSize, Vec3d sunColor, float multiplier) { if (sun != null && sun.isBlackHole()) { GlStateManager.enableAlpha(); @@ -742,86 +736,38 @@ protected void drawStar(BufferBuilder buffer, StellarBody sun, DimensionProperti GL11.glPushMatrix(); GL11.glTranslatef(0, 30, 0); - GL11.glDisable(GL11.GL_BLEND); GlStateManager.depthMask(true); + // Black hole sphere GL11.glPushMatrix(); GL11.glTranslatef(0, 100, 0); f10 = sunSize * 2f * AstronomicalBodyHelper.getBodySizeMultiplier(solarOrbitalDistance); - - mc.renderEngine.bindTexture(TextureResources.locationWhitePng); + bind(TextureResources.locationWhitePng); GlStateManager.disableCull(); - GlStateManager.color(skycolor[0], skycolor[1], skycolor[2]); // Set the color - renderSphere(0, 0, 0, f10, 16, 16); // Draw the sphere + GlStateManager.color(skycolor[0], skycolor[1], skycolor[2]); + renderSphere(0, 0, 0, f10, 16, 16); GlStateManager.enableCull(); GL11.glEnable(GL11.GL_BLEND); GL11.glDepthMask(false); GL11.glPopMatrix(); -/* - GL11.glPushMatrix(); - mc.renderEngine.bindTexture(TextureResources.locationBlackHole); - GL11.glTranslatef(0, 100, 0); - f10 = sunSize * 2f * AstronomicalBodyHelper.getBodySizeMultiplier(solarOrbitalDistance); - //float scale = 1 ; - //GL11.glRotatef(phase, 0, 1, 0); - //GL11.glScaled(scale, scale, scale); - GlStateManager.color((float) 1, (float) .5, (float) .4, 1f); - - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - //multiplier = 2; - buffer.pos(-f10, 0.0D, -f10).tex(0.0D, 0.0D).endVertex(); - buffer.pos(f10, 0.0D, -f10).tex(1.0D, 0.0D).endVertex(); - buffer.pos(f10, 0.0D, f10).tex(1.0D, 1.0D).endVertex(); - buffer.pos(-f10, 0.0D, f10).tex(0.0D, 1.0D).endVertex(); - Tessellator.getInstance().draw(); - GL11.glPopMatrix(); - - - GL11.glEnable(GL11.GL_BLEND); - GL11.glDepthMask(false); - - GL11.glPushMatrix(); - mc.renderEngine.bindTexture(TextureResources.locationBlackHoleBorder); - GL11.glTranslatef( 0, 99.8F, 0); - //GL11.glRotatef(phase, 0, 1, 0); - float scale = 1.1F; - GL11.glScaled(scale, scale, scale); - GlStateManager.color((float) 1, (float) .5, (float) .4, 1f); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - //multiplier = 2; - buffer.pos(-f10, 0.0D, -f10).tex(0.0D, 0.0D).endVertex(); - buffer.pos(f10, 0.0D, -f10).tex(1.0D, 0.0D).endVertex(); - buffer.pos(f10, 0.0D, f10).tex(1.0D, 1.0D).endVertex(); - buffer.pos(-f10, 0.0D, f10).tex(0.0D, 1.0D).endVertex(); - Tessellator.getInstance().draw(); - GL11.glPopMatrix(); -*/ float diskangle = sun.diskAngle; - float m = -xrotangle; - while (m > 360) - m-=360; - while (m < 0) - m+=360; - //Render accretion disk - mc.renderEngine.bindTexture(TextureResources.locationAccretionDiskDense); - GlStateManager.depthMask(false); + while (m > 360) m -= 360; + while (m < 0) m += 360; - float speedMult = 5; + // Dense inner disk - ORIGINAL ROTATIONS + bind(TextureResources.locationAccretionDiskDense); + GlStateManager.depthMask(false); GlStateManager.disableCull(); - GL11.glPushMatrix(); GL11.glTranslatef(0, 100, 0); GL11.glRotatef(90, 0f, 1f, 0f); - //GL11.glRotatef(m, 1f, 0f, 0f); - //GL11.glRotatef(diskangle, 0, 0, 1); - //GL11.glRotatef(90, 1, 0, 0); - GL11.glRotatef((System.currentTimeMillis() % (int) (360 * 360 * speedMult)) / (360f * speedMult), 0, 1, 0); - - GlStateManager.color((float) 1, (float) .7, (float) .55, 1f); + // Original rotation with speedMult = 5 + GL11.glRotatef((System.currentTimeMillis() % (int) (360 * 360 * 5)) / (360f * 5), 0, 1, 0); + GlStateManager.color(1f, .7f, .55f, 1f); buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); f10 = sunSize * 6.5f * AstronomicalBodyHelper.getBodySizeMultiplier(solarOrbitalDistance); buffer.pos(-f10, 0.0D, -f10).tex(0.0D, 0.0D).endVertex(); @@ -831,23 +777,23 @@ protected void drawStar(BufferBuilder buffer, StellarBody sun, DimensionProperti Tessellator.getInstance().draw(); GL11.glPopMatrix(); - - mc.renderEngine.bindTexture(TextureResources.locationAccretionDisk); - + // Outer translucent disks - COMPLEX ORIGINAL LOGIC + bind(TextureResources.locationAccretionDisk); for (int i = 0; i < 3; i++) { - speedMult = ((0) * 1.01f + 1)/0.1F; + float speedMult = 10.0f; // ORIGINAL CALCULATION: ((0) * 1.01f + 1)/0.1F + + // First layer - 100.01f GL11.glPushMatrix(); GL11.glTranslatef(0, 100.01f, 0); + // RESTORE ALL ORIGINAL ROTATIONS: GL11.glRotatef((float) currentplanetphi, 0f, 1f, 0f); GL11.glRotatef(m, 1f, 0f, 0f); GL11.glRotatef(diskangle, 0, 0, 1); GL11.glRotatef((System.currentTimeMillis() % (int) (speedMult * 36000)) / (100f * speedMult), 0, 1, 0); - - // make every disks angle slightly different - GL11.glRotatef(120*i, 0, 1, 0); + GL11.glRotatef(120 * i, 0, 1, 0); GL11.glRotatef(0.5f, 1, 0, 0); - GlStateManager.color((float) 1, (float) .5, (float) .4, 0.3f); + GlStateManager.color(1f, .5f, .4f, 0.3f); buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); f10 = sunSize * 40f * AstronomicalBodyHelper.getBodySizeMultiplier(solarOrbitalDistance); buffer.pos(-f10, 0.0D, -f10).tex(0.0D, 0.0D).endVertex(); @@ -857,22 +803,19 @@ protected void drawStar(BufferBuilder buffer, StellarBody sun, DimensionProperti Tessellator.getInstance().draw(); GL11.glPopMatrix(); - + // Second layer - 100f GL11.glPushMatrix(); - GL11.glTranslatef(0, 100f, 0); GL11.glRotatef((float) currentplanetphi, 0f, 1f, 0f); GL11.glRotatef(m, 1f, 0f, 0f); GL11.glRotatef(diskangle, 0, 0, 1); GL11.glRotatef((System.currentTimeMillis() % (int) (speedMult * 360 * 50)) / (50f * speedMult), 0, 1, 0); - // make every disks angle slightly different - GL11.glRotatef(120*i, 0, 1, 0); + GL11.glRotatef(120 * i, 0, 1, 0); GL11.glRotatef(0.5f, 1, 0, 0); - GlStateManager.color((float) 0.8, (float) .7, (float) .4, 0.3f); + GlStateManager.color(0.8f, .7f, .4f, 0.3f); buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); f10 = sunSize * 30f * AstronomicalBodyHelper.getBodySizeMultiplier(solarOrbitalDistance); - //multiplier = 2; buffer.pos(-f10, 0.0D, -f10).tex(0.0D, 0.0D).endVertex(); buffer.pos(f10, 0.0D, -f10).tex(1.0D, 0.0D).endVertex(); buffer.pos(f10, 0.0D, f10).tex(1.0D, 1.0D).endVertex(); @@ -880,47 +823,41 @@ protected void drawStar(BufferBuilder buffer, StellarBody sun, DimensionProperti Tessellator.getInstance().draw(); GL11.glPopMatrix(); + // Third layer - 99.99f GL11.glPushMatrix(); - GL11.glTranslatef(0, 99.99f, 0); GL11.glRotatef((float) currentplanetphi, 0f, 1f, 0f); GL11.glRotatef(m, 1f, 0f, 0f); GL11.glRotatef(diskangle, 0, 0, 1); GL11.glRotatef((System.currentTimeMillis() % (int) (speedMult * 360 * 25)) / (25f * speedMult), 0, 1, 0); - // make every disks angle slightly different - GL11.glRotatef(120*i, 0, 1, 0); + GL11.glRotatef(120 * i, 0, 1, 0); GL11.glRotatef(0.5f, 1, 0, 0); - GlStateManager.color((float) 0.2, (float) .4, (float) 1, 0.3f); + GlStateManager.color(0.2f, .4f, 1f, 0.3f); buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); f10 = sunSize * 15f * AstronomicalBodyHelper.getBodySizeMultiplier(solarOrbitalDistance); - //multiplier = 2; buffer.pos(-f10, 0.0D, -f10).tex(0.0D, 0.0D).endVertex(); buffer.pos(f10, 0.0D, -f10).tex(1.0D, 0.0D).endVertex(); buffer.pos(f10, 0.0D, f10).tex(1.0D, 1.0D).endVertex(); buffer.pos(-f10, 0.0D, f10).tex(0.0D, 1.0D).endVertex(); Tessellator.getInstance().draw(); GL11.glPopMatrix(); - - - - } + // ORIGINAL DEPTH MANAGEMENT GlStateManager.depthMask(true); GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); GlStateManager.depthMask(false); + GL11.glPopMatrix(); GlStateManager.enableCull(); - - + //GlStateManager.depthMask(true); // keep ? } else { - mc.renderEngine.bindTexture(TextureResources.locationSunPng); - //Set sun color and distance + // Regular star (quad) path + bind(TextureResources.locationSunPng); GlStateManager.color((float) sunColor.x, (float) sunColor.y, (float) sunColor.z, Math.min((multiplier) * 2f, 1f)); buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); float f10 = sunSize * 15f * AstronomicalBodyHelper.getBodySizeMultiplier(solarOrbitalDistance); - //multiplier = 2; buffer.pos(-f10, 120.0D, -f10).tex(0.0D, 0.0D).endVertex(); buffer.pos(f10, 120.0D, -f10).tex(1.0D, 0.0D).endVertex(); buffer.pos(f10, 120.0D, f10).tex(1.0D, 1.0D).endVertex(); diff --git a/src/main/java/zmaster587/advancedRocketry/command/ARCommandRoot.java b/src/main/java/zmaster587/advancedRocketry/command/ARCommandRoot.java new file mode 100644 index 000000000..e1e3aa3c9 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/ARCommandRoot.java @@ -0,0 +1,79 @@ +package zmaster587.advancedRocketry.command; + +import net.minecraft.command.ICommandSender; +import net.minecraftforge.server.command.CommandTreeBase; +import net.minecraftforge.server.command.CommandTreeHelp; +import zmaster587.advancedRocketry.command.sub.AddSealantCommand; +import zmaster587.advancedRocketry.command.sub.AddTorchCommand; +import zmaster587.advancedRocketry.command.sub.FillDataCommand; +import zmaster587.advancedRocketry.command.sub.ReloadRecipesCommand; +import zmaster587.advancedRocketry.command.sub.SetGravityCommand; +import zmaster587.advancedRocketry.command.sub.dev.DevCommand; +import zmaster587.advancedRocketry.command.sub.planet.PlanetCommand; +import zmaster587.advancedRocketry.command.sub.redirect.WeatherCommand; +import zmaster587.advancedRocketry.command.sub.star.StarCommand; +import zmaster587.advancedRocketry.command.sub.station.StationCommand; +import zmaster587.advancedRocketry.command.sub.teleport.FetchCommand; +import zmaster587.advancedRocketry.command.sub.teleport.GoToCommand; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class ARCommandRoot extends CommandTreeBase { + private final List aliases; + + public ARCommandRoot() { + aliases = new ArrayList<>(); + aliases.add("advancedrocketry"); + aliases.add("advrocketry"); + aliases.add("ar"); + + addSubcommand(new WeatherCommand()); + addSubcommand(new AddSealantCommand()); + addSubcommand(new AddTorchCommand()); + addSubcommand(new ReloadRecipesCommand()); + addSubcommand(new SetGravityCommand()); + addSubcommand(new FetchCommand()); + addSubcommand(new PlanetCommand()); + addSubcommand(new StarCommand()); + addSubcommand(new StationCommand()); + addSubcommand(new GoToCommand()); + addSubcommand(new FillDataCommand()); + addSubcommand(new DevCommand()); + + addSubcommand(new CommandTreeHelp(this)); + } + + @Override + public String getName() { + return "advancedrocketry"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "/advancedrocketry [subcommand]"; + } + + @Override + public List getAliases() { + return aliases; + } + + @Override + public int getRequiredPermissionLevel() { + return 2; + } + + public static String[] shiftArgs(@Nullable String[] s, int shift) + { + if(s == null || s.length - shift <= 0) + { + return new String[0]; + } + + String[] s1 = new String[s.length - shift]; + System.arraycopy(s, shift, s1, 0, s1.length); + return s1; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/WorldCommand.java b/src/main/java/zmaster587/advancedRocketry/command/WorldCommand.java deleted file mode 100644 index 3ae264070..000000000 --- a/src/main/java/zmaster587/advancedRocketry/command/WorldCommand.java +++ /dev/null @@ -1,991 +0,0 @@ -package zmaster587.advancedRocketry.command; - -import net.minecraft.block.Block; -import net.minecraft.command.ICommand; -import net.minecraft.command.ICommandSender; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.init.Blocks; -import net.minecraft.item.ItemStack; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.EnumHand; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.world.World; -import net.minecraft.world.WorldServer; -import net.minecraft.world.biome.Biome; -import net.minecraftforge.common.MinecraftForge; -import zmaster587.advancedRocketry.AdvancedRocketry; -import zmaster587.advancedRocketry.api.ARConfiguration; -import zmaster587.advancedRocketry.api.AdvancedRocketryAPI; -import zmaster587.advancedRocketry.api.AdvancedRocketryItems; -import zmaster587.advancedRocketry.api.DataStorage.DataType; -import zmaster587.advancedRocketry.api.dimension.IDimensionProperties; -import zmaster587.advancedRocketry.api.dimension.solar.StellarBody; -import zmaster587.advancedRocketry.api.stations.ISpaceObject; -import zmaster587.advancedRocketry.dimension.DimensionManager; -import zmaster587.advancedRocketry.dimension.DimensionProperties; -import zmaster587.advancedRocketry.integration.CompatibilityMgr; -import zmaster587.advancedRocketry.item.ItemData; -import zmaster587.advancedRocketry.item.ItemMultiData; -import zmaster587.advancedRocketry.item.ItemStationChip; -import zmaster587.advancedRocketry.network.PacketDimInfo; -import zmaster587.advancedRocketry.network.PacketStellarInfo; -import zmaster587.advancedRocketry.stations.SpaceObjectManager; -import zmaster587.advancedRocketry.unit.IngameTestOrchestrator; -import zmaster587.advancedRocketry.world.util.TeleporterNoPortal; -import zmaster587.advancedRocketry.world.util.TeleporterNoPortalSeekBlock; -import zmaster587.libVulpes.network.PacketHandler; -import zmaster587.libVulpes.util.HashedBlockPosition; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -public class WorldCommand implements ICommand { - - - private List aliases; - - public WorldCommand() { - - aliases = new ArrayList<>(); - aliases.add("advancedrocketry"); - aliases.add("advrocketry"); - aliases.add("ar"); - } - - @Override - @Nonnull - public String getName() { - return "advancedrocketry"; - } - - @Nonnull - @Override - public String getUsage(@Nullable ICommandSender sender) { - return "advancedrocketry help"; - } - - @Nonnull - @Override - public List getAliases() { - return aliases; - } - - private void commandAddTorch(ICommandSender sender, String[] cmdstring) { - - if (cmdstring.length >= 2 && cmdstring[1].equalsIgnoreCase("help")) { - sender.sendMessage(new TextComponentString(aliases.get(0) + " " + cmdstring[0] + " - Adds the currently held block to the list of objects that drop when there's no atmosphere")); - return; - } - - Entity player = sender.getCommandSenderEntity(); - if (!(player instanceof EntityPlayer)) { - sender.sendMessage(new TextComponentString("Not a player entity")); - return; - } - - Block block = Block.getBlockFromItem(((EntityPlayer) player).getHeldItemMainhand().getItem()); - if (block != Blocks.AIR) { - if (ARConfiguration.getCurrentConfig().torchBlocks.contains(block)) - sender.sendMessage(new TextComponentString(block.getLocalizedName() + " is already in the torch list")); - else { - - ARConfiguration.getCurrentConfig().addTorchblock(block); - - sender.sendMessage(new TextComponentString(block.getLocalizedName() + " added to the torch list")); - } - } else - sender.sendMessage(new TextComponentString("Held block cannot be added to torch list")); - } - - private void commandAddSolidBlockOverride(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length >= 2 && cmdstring[1].equalsIgnoreCase("help")) { - sender.sendMessage(new TextComponentString(aliases.get(0) + " " + cmdstring[0] + " - Adds the currently held block to the list of blocks that can hold a seal")); - return; - } - - Entity player = sender.getCommandSenderEntity(); - if (!(player instanceof EntityPlayer)) { - sender.sendMessage(new TextComponentString("Not a player entity")); - return; - } - - Block block = Block.getBlockFromItem(((EntityPlayer) player).getHeldItemMainhand().getItem()); - if (block != Blocks.AIR) { - if (ARConfiguration.getCurrentConfig().torchBlocks.contains(block)) - sender.sendMessage(new TextComponentString(block.getLocalizedName() + " is already in the sealed blocks list")); - else { - - ARConfiguration.getCurrentConfig().addSealedBlock(block); - - sender.sendMessage(new TextComponentString(block.getLocalizedName() + " added to the sealed block list")); - } - } else - sender.sendMessage(new TextComponentString("Held block cannot be added to sealed block list")); - } - - private void commandGiveStation(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length < 2 || cmdstring[1].equalsIgnoreCase("help")) { - sender.sendMessage(new TextComponentString(aliases.get(0) + " " + cmdstring[0] + " - Gives the player playerName (if supplied) a spacestation with ID stationID")); - sender.sendMessage(new TextComponentString("Usage: /advrocketry " + cmdstring[0] + " [PlayerName]")); - return; - } - - EntityPlayer player = null; - if (cmdstring.length >= 3) { - player = getPlayerByName(cmdstring[2]); - if (player == null) { - sender.sendMessage(new TextComponentString("Player " + cmdstring[2] + " not found")); - return; - } - } else if (sender.getCommandSenderEntity() != null) - player = ((EntityPlayer) sender); - - if (player != null) { - int stationId = Integer.parseInt(cmdstring[1]); - ItemStack stack = new ItemStack(AdvancedRocketryItems.itemSpaceStationChip); - ItemStationChip.setUUID(stack, stationId); - player.inventory.addItemStackToInventory(stack); - } else - sender.sendMessage(new TextComponentString("Usage: /advrocketry " + cmdstring[0] + " [PlayerName]")); - } - - private void commandFillData(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length < 2) - return; - - ItemStack stack; - if (sender.getCommandSenderEntity() != null) { - stack = ((EntityPlayer) sender.getCommandSenderEntity()).getHeldItem(EnumHand.MAIN_HAND); - - if (cmdstring[1].equalsIgnoreCase("help")) { - sender.sendMessage(new TextComponentString(aliases.get(0) + " " + cmdstring[0] + " [datatype] [amountFill]\n")); - sender.sendMessage(new TextComponentString("Fills the amount of the data type specifies into the chip being held.")); - sender.sendMessage(new TextComponentString("If the datatype is not specified then command fills all datatypes, if no amountFill is specified completely fills the chip")); - return; - } - - if (!stack.isEmpty() && stack.getItem() instanceof ItemData) { - ItemData item = (ItemData) stack.getItem(); - int dataAmount = item.getMaxData(stack.getItemDamage()); - DataType dataType; - - try { - dataType = DataType.valueOf(cmdstring[1].toUpperCase(Locale.ENGLISH)); - } catch (IllegalArgumentException e) { - sender.sendMessage(new TextComponentString("Did you mean: /advrocketry" + cmdstring[0] + " [datatype] [amountFill]")); - sender.sendMessage(new TextComponentString("Not a valid datatype")); - StringBuilder value = new StringBuilder(); - for (DataType data : DataType.values()) { - if (!data.name().equals("UNDEFINED")) { - value.append(data.name().toLowerCase()).append(", "); - } - } - - sender.sendMessage(new TextComponentString("Try " + value)); - return; - } - if (cmdstring.length >= 3) - try { - dataAmount = Integer.parseInt(cmdstring[2]); - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("Did you mean: /advrocketry" + cmdstring[0] + " [datatype] [amountFill]")); - sender.sendMessage(new TextComponentString("Not a valid number")); - return; - } - - item.setData(stack, dataAmount, dataType); - sender.sendMessage(new TextComponentString("Data filled!")); - } else if (!stack.isEmpty() && stack.getItem() instanceof ItemMultiData) { - ItemMultiData item = (ItemMultiData) stack.getItem(); - int dataAmount = item.getMaxData(stack); - DataType dataType; - - try { - dataType = DataType.valueOf(cmdstring[1].toUpperCase(Locale.ENGLISH)); - } catch (IllegalArgumentException e) { - sender.sendMessage(new TextComponentString("Did you mean: /advrocketry" + cmdstring[0] + " [datatype] [amountFill]")); - sender.sendMessage(new TextComponentString("Not a valid datatype")); - StringBuilder value = new StringBuilder(); - for (DataType data : DataType.values()) { - if (!data.name().equals("UNDEFINED")) { - value.append(data.name().toLowerCase()).append(", "); - } - } - - sender.sendMessage(new TextComponentString("Try " + value)); - return; - } - if (cmdstring.length >= 3) - try { - dataAmount = Integer.parseInt(cmdstring[2]); - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("Did you mean: /advrocketry" + cmdstring[0] + " [datatype] [amountFill]")); - sender.sendMessage(new TextComponentString("Not a valid number")); - return; - } - - item.setData(stack, dataAmount, dataType); - - sender.sendMessage(new TextComponentString("Data filled!")); - } else - sender.sendMessage(new TextComponentString("Not holding data item")); - } else - sender.sendMessage(new TextComponentString("Ghosts don't have items!")); - } - - - private void commandReloadRecipes(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length >= 2 && cmdstring[1].equalsIgnoreCase("help")) { - sender.sendMessage(new TextComponentString(aliases.get(0) + " " + cmdstring[0] + " - Reloads recipes from the XML files in the config folder")); - return; - } - - try { - AdvancedRocketry.machineRecipes.clearAllMachineRecipes(); - AdvancedRocketry.machineRecipes.registerAllMachineRecipes(); - AdvancedRocketry.machineRecipes.createAutoGennedRecipes(AdvancedRocketry.modProducts); - AdvancedRocketry.machineRecipes.registerXMLRecipes(); - - sender.sendMessage(new TextComponentString("Recipes reloaded")); - - CompatibilityMgr.reloadRecipes(); - } catch (Exception e) { - e.printStackTrace(); - sender.sendMessage(new TextComponentString("Serious error has occurred! Possible recipe corruption")); - sender.sendMessage(new TextComponentString("Please check logs!")); - sender.sendMessage(new TextComponentString("You may be able to rectify this error by repairing the XML and/or restarting the game")); - } - } - - private void commandSetGravity(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length >= 2) { - if (cmdstring[1].equalsIgnoreCase("help")) { - sender.sendMessage(new TextComponentString(cmdstring[0] + " - sets your gravity to amount where 1 is Earth-like")); - return; - } - if (sender instanceof Entity) { - Entity player; - if (cmdstring.length > 2) - player = sender.getServer().getPlayerList().getPlayerByUsername(cmdstring[2]); - else - player = (Entity) sender; - if (player != null) { - try { - double d = Double.parseDouble(cmdstring[1]); - if (d == 0) - AdvancedRocketryAPI.gravityManager.clearGravityEffect(player); - else - AdvancedRocketryAPI.gravityManager.setGravityMultiplier((Entity) sender, d); - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString(cmdstring[1] + " is not a valid number")); - } - } else { - sender.sendMessage(new TextComponentString("Not a valid player")); - } - } else { - sender.sendMessage(new TextComponentString("Not a valid player")); - } - } else { - sender.sendMessage(new TextComponentString(aliases.get(0) + " " + cmdstring[0] + " gravity_multiplier [playerName]")); - sender.sendMessage(new TextComponentString("")); - sender.sendMessage(new TextComponentString("Use 0 as the gravity_multiplier to allow regular planet gravity to take over")); - } - } - - private void commandGoto(ICommandSender sender, String[] cmdstring) { - EntityPlayer player; - if (sender instanceof Entity && (player = sender.getEntityWorld().getPlayerEntityByName(sender.getName())) != null) { - if (cmdstring.length < 2 || cmdstring[1].equalsIgnoreCase("help")) { - sender.sendMessage(new TextComponentString(cmdstring[0] + " - teleports the player to the supplied dimension")); - sender.sendMessage(new TextComponentString(cmdstring[0] + " station - teleports the player to the supplied station")); - return; - } - try { - int dim; - - if (cmdstring.length == 2) { - dim = Integer.parseInt(cmdstring[1]); - if (net.minecraftforge.common.DimensionManager.isDimensionRegistered(dim)) { - if (net.minecraftforge.common.DimensionManager.getWorld(dim) == null) { - net.minecraftforge.common.DimensionManager.initDimension(dim); - } - player.getServer().getPlayerList().transferPlayerToDimension((EntityPlayerMP) player, dim, new TeleporterNoPortalSeekBlock(net.minecraftforge.common.DimensionManager.getWorld(dim))); - } else - sender.sendMessage(new TextComponentString("Dimension does not exist")); - } else if (cmdstring[1].equalsIgnoreCase("station")) { - dim = ARConfiguration.getCurrentConfig().spaceDimId; - int stationId = Integer.parseInt(cmdstring[2]); - ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStation(stationId); - - if (spaceObject != null) { - if (player.world.provider.getDimension() != ARConfiguration.getCurrentConfig().spaceDimId) - player.getServer().getPlayerList().transferPlayerToDimension((EntityPlayerMP) player, dim, new TeleporterNoPortal((WorldServer) player.world)); - HashedBlockPosition vec = spaceObject.getSpawnLocation(); - player.setPositionAndUpdate(vec.x, vec.y, vec.z); - } else { - sender.sendMessage(new TextComponentString("Station " + stationId + " does not exist!")); - } - } - - - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString(cmdstring[0] + " ")); - sender.sendMessage(new TextComponentString(cmdstring[0] + "station ")); - } - } else - sender.sendMessage(new TextComponentString("Must be a player to use this command")); - } - - private void commandFetch(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length < 2) - return; - - EntityPlayer me = (EntityPlayer) sender.getCommandSenderEntity(); - EntityPlayer player = getPlayerByName(cmdstring[1]); - System.out.println(cmdstring[1] + " " + sender.getCommandSenderEntity()); - - if (player == null) { - sender.sendMessage(new TextComponentString("Invalid player name: " + cmdstring[1])); - } else { - player.getServer().getPlayerList().transferPlayerToDimension((EntityPlayerMP) player, me.world.provider.getDimension(), new TeleporterNoPortal(me.getServer().getWorld(me.world.provider.getDimension()))); - player.setPosition(me.posX, me.posY, me.posZ); - } - } - - private void commandPlanetList(ICommandSender sender, String[] cmdstring) { - sender.sendMessage(new TextComponentString("Dimensions:")); - for (int i : DimensionManager.getInstance().getRegisteredDimensions()) { - sender.sendMessage(new TextComponentString("DIM" + i + ": " + DimensionManager.getInstance().getDimensionProperties(i).getName())); - } - } - - private void commandPlanetHelp(ICommandSender sender, String[] cmdstring) { - sender.sendMessage(new TextComponentString("Planet:")); - sender.sendMessage(new TextComponentString("planet delete [dimid]")); - sender.sendMessage(new TextComponentString("planet generate [starId] (moon/gas) [name] [atmosphere randomness] [distance Randomness] [gravity randomness] (atmosphere base) (distance base) (gravity base)")); - sender.sendMessage(new TextComponentString("planet list")); - sender.sendMessage(new TextComponentString("planet reset [dimid]")); - sender.sendMessage(new TextComponentString("planet set [property]")); - sender.sendMessage(new TextComponentString("planet get [property]")); - } - - private void commandPlanetReset(ICommandSender sender, String[] cmdstring) { - int dimId; - if (cmdstring.length == 3) { - try { - dimId = Integer.parseInt(cmdstring[2]); - DimensionManager.getInstance().getDimensionProperties(dimId).resetProperties(); - PacketHandler.sendToAll(new PacketDimInfo(dimId, DimensionManager.getInstance().getDimensionProperties(dimId))); - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("Invalid dimId")); - } - } else if (cmdstring.length == 2) { - if (sender.getCommandSenderEntity() != null) { - if (DimensionManager.getInstance().isDimensionCreated((dimId = sender.getEntityWorld().provider.getDimension()))) { - DimensionManager.getInstance().getDimensionProperties(dimId).resetProperties(); - PacketHandler.sendToAll(new PacketDimInfo(dimId, DimensionManager.getInstance().getDimensionProperties(dimId))); - } - } else { - sender.sendMessage(new TextComponentString("Please specify dimension ID")); - } - } - } - - private void commandPlanetDelete(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length == 3) { - int deletedDimId; - try { - deletedDimId = Integer.parseInt(cmdstring[2]); - - if (DimensionManager.getInstance().isDimensionCreated(deletedDimId)) { - - if (net.minecraftforge.common.DimensionManager.getWorld(deletedDimId) == null || net.minecraftforge.common.DimensionManager.getWorld(deletedDimId).playerEntities.isEmpty()) { - DimensionManager.getInstance().deleteDimension(deletedDimId); - PacketHandler.sendToAll(new PacketDimInfo(deletedDimId, null)); - sender.sendMessage(new TextComponentString("Dim " + deletedDimId + " deleted!")); - } else { - //If the world still has players abort and list players - sender.sendMessage(new TextComponentString("World still has players:")); - - for (EntityPlayer player : net.minecraftforge.common.DimensionManager.getWorld(deletedDimId).playerEntities) { - sender.sendMessage(player.getDisplayName()); - } - - } - - - } else { - sender.sendMessage(new TextComponentString("Dimension does not exist")); - } - - } catch (NumberFormatException exception) { - sender.sendMessage(new TextComponentString("Invalid argument")); - } - } else { - sender.sendMessage(new TextComponentString(cmdstring[0] + " " + cmdstring[1] + " " + cmdstring[2] + " ")); - } - } - - private void commandPlanetGenerate(ICommandSender sender, String[] cmdstring) { - int gasOffset = 0; - boolean gassy = false; - boolean moon = false; - int starId = 0; - - if (cmdstring.length > 2) { - try { - starId = Integer.parseInt(cmdstring[2]); - gasOffset++; - } catch (NumberFormatException e) { - e.printStackTrace(); - sender.sendMessage(new TextComponentString("Failed to parse integer " + cmdstring[2])); - return; - } - } - - if (cmdstring.length > 2 + gasOffset) { - if (cmdstring[2 + gasOffset].equalsIgnoreCase("moon")) { - gasOffset++; - moon = true; - - if (!DimensionManager.getInstance().isDimensionCreated(starId)) { - sender.sendMessage(new TextComponentString("Invalid planet ID")); - sender.sendMessage(new TextComponentString(cmdstring[0] + " " + cmdstring[1] + "[planetId] [moon] [gas] ")); - return; - } - } else if (DimensionManager.getInstance().getStar(starId) == null) { - sender.sendMessage(new TextComponentString("Invalid star ID")); - sender.sendMessage(new TextComponentString(cmdstring[0] + " " + cmdstring[1] + "[starId] [gas] ")); - - return; - } - } - - if (cmdstring.length > 2 + gasOffset && cmdstring[2 + gasOffset].equalsIgnoreCase("gas")) { - gasOffset++; - gassy = true; - } - - try { - //advancedrocketry planet generate - if (cmdstring.length == 6 + gasOffset) { - - int planetId = starId; - if (moon) - starId = DimensionManager.getInstance().getDimensionProperties(planetId).getStarId(); - - DimensionProperties properties; - if (!gassy) - properties = DimensionManager.getInstance().generateRandom(starId, cmdstring[2 + gasOffset], Integer.parseInt(cmdstring[3 + gasOffset]), Integer.parseInt(cmdstring[4 + gasOffset]), Integer.parseInt(cmdstring[5 + gasOffset])); - else - properties = DimensionManager.getInstance().generateRandomGasGiant(starId, cmdstring[2 + gasOffset], Integer.parseInt(cmdstring[3 + gasOffset]), Integer.parseInt(cmdstring[4 + gasOffset]), Integer.parseInt(cmdstring[5 + gasOffset]), 1, 1, 1); - - if (properties == null) - sender.sendMessage(new TextComponentString("Dimension: " + cmdstring[2 + gasOffset] + " failed to generate!")); - else - sender.sendMessage(new TextComponentString("Dimension: " + cmdstring[2 + gasOffset] + " generated!")); - - if (moon) { - properties.setParentPlanet(DimensionManager.getInstance().getDimensionProperties(planetId)); - DimensionManager.getInstance().getStar(starId).removePlanet(properties); - } - - sender.sendMessage(new TextComponentString("Dimension generated!")); - } else if (cmdstring.length == 9 + gasOffset) { - - int planetId = starId; - if (moon) - starId = DimensionManager.getInstance().getDimensionProperties(planetId).getStarId(); - - DimensionProperties properties; - - if (!gassy) - properties = DimensionManager.getInstance().generateRandom(starId, cmdstring[2 + gasOffset], Integer.parseInt(cmdstring[3 + gasOffset]), Integer.parseInt(cmdstring[4 + gasOffset]), Integer.parseInt(cmdstring[5 + gasOffset]), Integer.parseInt(cmdstring[6 + gasOffset]), Integer.parseInt(cmdstring[7 + gasOffset]), Integer.parseInt(cmdstring[8 + gasOffset])); - else - properties = DimensionManager.getInstance().generateRandomGasGiant(starId, cmdstring[2 + gasOffset], Integer.parseInt(cmdstring[3 + gasOffset]), Integer.parseInt(cmdstring[4 + gasOffset]), Integer.parseInt(cmdstring[5 + gasOffset]), Integer.parseInt(cmdstring[6 + gasOffset]), Integer.parseInt(cmdstring[7 + gasOffset]), Integer.parseInt(cmdstring[8 + gasOffset])); - - if (properties == null) - sender.sendMessage(new TextComponentString("Dimension: " + cmdstring[2 + gasOffset] + " failed to generate!")); - else - sender.sendMessage(new TextComponentString("Dimension: " + cmdstring[2 + gasOffset] + " generated!")); - - if (moon) { - properties.setParentPlanet(DimensionManager.getInstance().getDimensionProperties(planetId)); - DimensionManager.getInstance().getStar(starId).removePlanet(properties); - } - } else { - sender.sendMessage(new TextComponentString(cmdstring[0] + " " + cmdstring[1] + " [starId] [moon] [gas] ")); - sender.sendMessage(new TextComponentString("")); - sender.sendMessage(new TextComponentString(cmdstring[0] + " " + cmdstring[1] + " [starId] [moon] [gas] ")); - } - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString(cmdstring[0] + " " + cmdstring[1] + " [starId] [moon] [gas] ")); - sender.sendMessage(new TextComponentString("")); - sender.sendMessage(new TextComponentString(cmdstring[0] + " " + cmdstring[1] + " [starId] [moon] [gas] ")); - } - } - - private void commandPlanetSet(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length < 3) - return; - - int dimId; - if (!DimensionManager.getInstance().isDimensionCreated((dimId = sender.getEntityWorld().provider.getDimension()))) - return; - - int commandOffset = 0; - - if (cmdstring.length > 3) { - try { - dimId = Integer.parseInt(cmdstring[2]); - commandOffset = 1; - } catch (NumberFormatException e) { - //XXX: Ugh... ordering sucks, what properties does the dimension class have if you don't know what dim it is yet? - //sender.sendMessage(new TextComponentString("Invalid Property or Dimension")); - } - } - - if (!DimensionManager.getInstance().isDimensionCreated(dimId)) { - sender.sendMessage(new TextComponentString("Invalid dimensions")); - return; - } - - DimensionProperties properties = DimensionManager.getInstance().getDimensionProperties(dimId); - try { - if (cmdstring[2 + commandOffset].equalsIgnoreCase("atmosphereDensity")) { - properties.setAtmosphereDensityDirect(Integer.parseUnsignedInt(cmdstring[3 + commandOffset])); - sender.sendMessage(new TextComponentString("Setting " + cmdstring[2 + commandOffset] + " for dimension " + dimId + " to " + cmdstring[3 + commandOffset])); - } else { - - Field field = properties.getClass().getDeclaredField(cmdstring[2 + commandOffset]); - - if (field.getType().isArray()) { - - if (Float.TYPE == field.getType().getComponentType()) { - float[] var = (float[]) field.get(properties); - - if (cmdstring.length - 3 - commandOffset == var.length) { - - //Make sure we catch if some invalid arg is entered - StringBuilder outString = new StringBuilder(); - for (int i = 0; i < var.length; i++) { - var[i] = Float.parseFloat(cmdstring[3 + i + commandOffset]); - outString.append(cmdstring[3 + i + commandOffset]).append(" "); - } - - field.set(properties, var); - sender.sendMessage(new TextComponentString("Setting " + cmdstring[2 + commandOffset] + " for dimension " + dimId + " to " + outString)); - } - } - - if (Integer.TYPE == field.getType().getComponentType()) { - int[] var = (int[]) field.get(properties); - - if (cmdstring.length - 3 - commandOffset == var.length) { - - //Make sure we catch if some invalid arg is entered - StringBuilder outString = new StringBuilder(); - for (int i = 0; i < var.length; i++) { - var[i] = Integer.parseInt(cmdstring[3 + i + commandOffset]); - outString.append(cmdstring[3 + i + commandOffset]).append(" "); - } - - field.set(properties, var); - sender.sendMessage(new TextComponentString("Setting " + cmdstring[2 + commandOffset] + " for dimension " + dimId + " to " + outString)); - } - } - } else { - if (Integer.TYPE == field.getType()) - field.set(properties, Integer.parseInt(cmdstring[3 + commandOffset])); - else if (Float.TYPE == field.getType()) - field.set(properties, Float.parseFloat(cmdstring[3 + commandOffset])); - else if (Double.TYPE == field.getType()) - field.set(properties, Double.parseDouble(cmdstring[3 + commandOffset])); - else if (Boolean.TYPE == field.getType()) - field.set(properties, Boolean.parseBoolean(cmdstring[3 + commandOffset])); - else - field.set(properties, cmdstring[3 + commandOffset]); - sender.sendMessage(new TextComponentString("Setting " + cmdstring[2 + commandOffset] + " for dimension " + dimId + " to " + cmdstring[3 + commandOffset])); - } - - } - PacketHandler.sendToAll(new PacketDimInfo(dimId, properties)); - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("Invalid argument for parameter " + cmdstring[2 + commandOffset])); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void commandPlanetGet(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length < 3) - return; - - int dimId; - if (!DimensionManager.getInstance().isDimensionCreated((dimId = sender.getEntityWorld().provider.getDimension()))) - return; - int commandOffset = 0; - if (cmdstring.length > 3) { - try { - dimId = Integer.parseInt(cmdstring[2]); - commandOffset = 1; - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("Invalid dimensions")); - } - } - - if (!DimensionManager.getInstance().isDimensionCreated(dimId)) { - sender.sendMessage(new TextComponentString("Invalid dimensions")); - return; - } - - DimensionProperties properties = DimensionManager.getInstance().getDimensionProperties(dimId); - if (cmdstring[2 + commandOffset].equalsIgnoreCase("atmosphereDensity")) { - sender.sendMessage(new TextComponentString(Integer.toString(properties.getAtmosphereDensity()))); - } else { - try { - Field field = properties.getClass().getDeclaredField(cmdstring[2 + commandOffset]); - - sender.sendMessage(new TextComponentString(field.get(properties).toString())); - - } catch (Exception e) { - - e.printStackTrace(); - sender.sendMessage(new TextComponentString("An error has occurred, please check logs")); - } - } - } - - private void commandStarGet(ICommandSender sender, String[] cmdstring) { - try { - int id = Integer.parseInt(cmdstring[3]); - StellarBody star = DimensionManager.getInstance().getStar(id); - if (star == null) - sender.sendMessage(new TextComponentString("Error: " + cmdstring[3] + " is not a valid star ID")); - else { - if (cmdstring[2].equalsIgnoreCase("temp")) { - sender.sendMessage(new TextComponentString("Temp: " + star.getTemperature())); - } else if (cmdstring[2].equalsIgnoreCase("planets")) { - sender.sendMessage(new TextComponentString("Planets orbiting the star:")); - for (IDimensionProperties planets : star.getPlanets()) { - sender.sendMessage(new TextComponentString("ID: " + planets.getId() + " : " + planets.getName())); - } - } else if (cmdstring[2].equalsIgnoreCase("pos")) { - sender.sendMessage(new TextComponentString("Pos: " + star.getPosX() + "," + star.getPosZ())); - } - }// end star existance validation - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("Error: " + cmdstring[3] + " is not a valid star ID")); - } - } - - private void commandStarSet(ICommandSender sender, String[] cmdstring) { - try { - int id = Integer.parseInt(cmdstring[3]); - StellarBody star = DimensionManager.getInstance().getStar(id); - if (star == null) - sender.sendMessage(new TextComponentString("Error: " + cmdstring[3] + " is not a valid star ID")); - else { - if (cmdstring[2].equalsIgnoreCase("temp")) { - try { - star.setTemperature(Integer.parseInt(cmdstring[4])); - sender.sendMessage(new TextComponentString("Temp set to " + star.getTemperature())); - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("star set temp ")); - } - } else if (cmdstring.length > 5 && cmdstring[2].equalsIgnoreCase("pos")) { - try { - int x = Integer.parseInt(cmdstring[4]); - int z = Integer.parseInt(cmdstring[5]); - star.setPosX(x); - star.setPosZ(z); - sender.sendMessage(new TextComponentString("Position set to " + x + "," + z)); - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("star set pos ")); - } - } - }// end star existance validation - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("Error: " + cmdstring[3] + " is not a valid star ID")); - } - } - - private void commandBiomeDump(ICommandSender sender, String[] cmdstring) { - - if (cmdstring.length >= 2 && cmdstring[1].compareToIgnoreCase("help") == 0) { - sender.sendMessage(new TextComponentString("Developer command: Dumps biome info to BiomeDump.txt!")); - return; - } - - try { - File file = new File("./BiomeDump.txt"); - if (!file.exists()) - file.createNewFile(); - - BufferedWriter writer = new BufferedWriter(new FileWriter(file)); - - writer.append("ID\tResource name\n"); - for (ResourceLocation resource : Biome.REGISTRY.getKeys()) { - writer.append(String.valueOf(Biome.getIdForBiome(Biome.REGISTRY.getObject(resource)))).append("\t").append(resource.toString()).append("\n"); - } - - writer.close(); - sender.sendMessage(new TextComponentString("The file \"BiomeDump.txt\" has been written to the current directory")); - } catch (Exception e) { - sender.sendMessage(new TextComponentString("An error has occurred writing to the file")); - } - } - - private void commandStarGenerate(ICommandSender sender, String[] cmdstring) { - try { - String name = cmdstring[2]; - int temp = Integer.parseInt(cmdstring[3]); - int x = Integer.parseInt(cmdstring[4]); - int z = Integer.parseInt(cmdstring[5]); - StellarBody star = new StellarBody(); - star.setTemperature(temp); - star.setPosX(x); - star.setPosZ(z); - star.setName(name); - star.setId(DimensionManager.getInstance().getNextFreeStarId()); - if (star.getId() != -1) { - DimensionManager.getInstance().addStar(star); - PacketHandler.sendToAll(new PacketStellarInfo(star.getId(), star)); - sender.sendMessage(new TextComponentString("Star added!")); - } else - sender.sendMessage(new TextComponentString("Why can't I hold all these stars! (either you have an insane number of stars or something really broke!)")); - - } catch (NumberFormatException e) { - sender.sendMessage(new TextComponentString("star generate ")); - } - } - - private void commandBeginTest(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length >= 2 && cmdstring[1].equalsIgnoreCase("help")) { - sender.sendMessage(new TextComponentString("Developer command: Runs system tests, debug only!")); - return; - } - if (sender.getCommandSenderEntity() != null) { - if (!IngameTestOrchestrator.registered) - MinecraftForge.EVENT_BUS.register(IngameTestOrchestrator.instance); - EntityPlayer player = ((EntityPlayer) sender); - IngameTestOrchestrator.runTests(player.getEntityWorld(), player); - } - } - - private void commandPlanet(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length < 2) { - commandPlanetHelp(sender, cmdstring); - return; - } - - switch (cmdstring[1].toLowerCase()) { - case "reset": - commandPlanetReset(sender, cmdstring); - break; - case "list": - commandPlanetList(sender, cmdstring); - break; - case "delete": - commandPlanetDelete(sender, cmdstring); - break; - case "generate": - commandPlanetGenerate(sender, cmdstring); - break; - case "set": - commandPlanetSet(sender, cmdstring); - break; - case "get": - commandPlanetGet(sender, cmdstring); - break; - case "help": - default: - commandPlanetHelp(sender, cmdstring); - } - } - - private void commandStar(ICommandSender sender, String[] cmdstring) { - if (cmdstring.length > 1) { - if (cmdstring[1].equalsIgnoreCase("list")) { - for (StellarBody star : DimensionManager.getInstance().getStars()) - sender.sendMessage(new TextComponentString(String.format("Star ID: %d Name: %s Num Planets: %d", star.getId(), star.getName(), star.getNumPlanets()))); - } else if (cmdstring[1].equalsIgnoreCase("help")) { - printStarHelp(sender); - } - } - if (cmdstring.length > 3) { - if (cmdstring[1].equalsIgnoreCase("get")) { - commandStarGet(sender, cmdstring); - } //get - } - if (cmdstring.length > 4) { - if (cmdstring[1].equalsIgnoreCase("set")) { - commandStarSet(sender, cmdstring); - } - } - if (cmdstring.length > 5) { - if (cmdstring[1].equalsIgnoreCase("generate")) { - commandStarGenerate(sender, cmdstring); - } - } - } - - @Override - @ParametersAreNonnullByDefault - public void execute(MinecraftServer server, ICommandSender sender, String[] string) { - - //advrocketry planet set - int opLevel = 2; - if (string.length == 0 || string[0].equalsIgnoreCase("help")) { - sender.sendMessage(new TextComponentString("Subcommands:")); - sender.sendMessage(new TextComponentString("planet")); - sender.sendMessage(new TextComponentString("fillData")); - sender.sendMessage(new TextComponentString("goto")); - sender.sendMessage(new TextComponentString("star")); - sender.sendMessage(new TextComponentString("fetch")); - sender.sendMessage(new TextComponentString("giveStation")); - sender.sendMessage(new TextComponentString("reloadRecipes")); - sender.sendMessage(new TextComponentString("setGravity")); - sender.sendMessage(new TextComponentString("addTorch")); - sender.sendMessage(new TextComponentString("[Enter /advrocketry help for more info]")); - //print help and return - return; - } - - switch (string[0].toLowerCase()) { - case "dumpbiomes": - commandBiomeDump(sender, string); - break; - case "begintest": - commandBeginTest(sender, string); - break; - case "addtorch": - commandAddTorch(sender, string); - break; - case "addsolidblockoverride": - commandAddSolidBlockOverride(sender, string); - break; - case "givestation": - commandGiveStation(sender, string); - break; - case "filldata": - commandFillData(sender, string); - break; - case "reloadrecipes": - commandReloadRecipes(sender, string); - break; - case "setgravity": - commandSetGravity(sender, string); - break; - case "goto": - commandGoto(sender, string); - break; - case "fetch": - commandFetch(sender, string); - break; - case "planet": - commandPlanet(sender, string); - break; - case "star": - commandStar(sender, string); - break; - } - } - - private void printStarHelp(ICommandSender sender) { - sender.sendMessage(new TextComponentString("star list")); - sender.sendMessage(new TextComponentString("star get temp ")); - sender.sendMessage(new TextComponentString("star get planets ")); - sender.sendMessage(new TextComponentString("star get pos ")); - sender.sendMessage(new TextComponentString("star set temp ")); - sender.sendMessage(new TextComponentString("star set pos ")); - sender.sendMessage(new TextComponentString("star generate ")); - } - - @Override - public boolean checkPermission(@Nonnull MinecraftServer server, ICommandSender sender) { - return sender.canUseCommand(2, getName()); - - } - - @Override - @Nonnull - @ParametersAreNonnullByDefault - public List getTabCompletions(MinecraftServer server, - ICommandSender sender, String[] string, @Nullable BlockPos targetPos) { - ArrayList list = new ArrayList<>(); - - if (string.length == 1) { - list.add("beginTest"); - list.add("planet"); - list.add("goto"); - list.add("fetch"); - list.add("star"); - list.add("fillData"); - list.add("setGravity"); - list.add("reloadRecipes"); - list.add("giveStation"); - list.add("dumpBiomes"); - list.add("addTorch"); - list.add("addSolidBlockOverride"); - } else if (string.length == 2) { - ArrayList list2 = new ArrayList<>(); - list2.add("get"); - list2.add("set"); - list2.add("list"); - list2.add("generate"); - if (string[0].equalsIgnoreCase("planet")) { - list2.add("reset"); - list2.add("new"); - list2.add("delete"); - - - for (String str : list2) { - if (str.startsWith(string[1])) - list.add(str); - } - } - } else if ((string[1].equalsIgnoreCase("get") || string[1].equalsIgnoreCase("set")) && string[0].equalsIgnoreCase("planet") && string.length == 3) { - for (Field field : DimensionProperties.class.getFields()) { - if (field.getName().startsWith(string[2])) - list.add(field.getName()); - - } - list.add("atmosphereDensity"); - } - - return list; - } - - @Override - public boolean isUsernameIndex(@Nonnull String[] string, int number) { - return number == 1 && string[0].equalsIgnoreCase("fetch"); - } - - @Override - public int compareTo(ICommand arg0) { - return this.getName().compareTo(arg0.getName()); - } - - private EntityPlayer getPlayerByName(String name) { - EntityPlayer player = null; - for (World world : net.minecraftforge.common.DimensionManager.getWorlds()) { - player = world.getPlayerEntityByName(name); - if (player != null) break; - } - - return player; - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/command/package-info.java b/src/main/java/zmaster587/advancedRocketry/command/package-info.java new file mode 100644 index 000000000..d561f1c5e --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package zmaster587.advancedRocketry.command; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/ARCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/ARCommand.java new file mode 100644 index 000000000..9ab87cb0e --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/ARCommand.java @@ -0,0 +1,21 @@ +package zmaster587.advancedRocketry.command.sub; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.WrongUsageException; +import org.apache.commons.lang3.StringUtils; + +public abstract class ARCommand extends CommandBase { + protected CommandException invalidValue(String name, int value) { + return new CommandException("commands.advancedrocketry.invalid", StringUtils.capitalize(name), value); + } + + protected CommandException invalidValue(String name, String value) { + return new CommandException("commands.advancedrocketry.invalid", StringUtils.capitalize(name), value); + } + + protected WrongUsageException wrongUsage(ICommandSender sender) { + return new WrongUsageException(getUsage(sender)); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/AddSealantCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/AddSealantCommand.java new file mode 100644 index 000000000..3f22c13a7 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/AddSealantCommand.java @@ -0,0 +1,48 @@ +package zmaster587.advancedRocketry.command.sub; + +import net.minecraft.block.Block; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Blocks; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.TextComponentTranslation; +import zmaster587.advancedRocketry.api.ARConfiguration; +import zmaster587.advancedRocketry.util.SealableBlockHandler; + +import java.util.Collections; +import java.util.List; + +public class AddSealantCommand extends ARCommand { + @Override + public String getName() { + return "addSealant"; + } + + @Override + public List getAliases() { + return Collections.singletonList("addsealant"); + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.addsealant.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length != 0) { + throw wrongUsage(sender); + } + EntityPlayerMP player = getCommandSenderAsPlayer(sender); + Block block = Block.getBlockFromItem(player.getHeldItemMainhand().getItem()); + if (block == Blocks.AIR) { + throw new CommandException("commands.advancedrocketry.addsealant.invalid"); + } + if (SealableBlockHandler.INSTANCE.getOverriddenSealableBlocks().contains(block)) { + throw new CommandException("commands.advancedrocketry.addsealant.exists", block.getLocalizedName()); + } + ARConfiguration.getCurrentConfig().addSealedBlock(block); + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.addsealant.success", block.getLocalizedName())); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/AddTorchCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/AddTorchCommand.java new file mode 100644 index 000000000..8e165f8eb --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/AddTorchCommand.java @@ -0,0 +1,46 @@ +package zmaster587.advancedRocketry.command.sub; + +import net.minecraft.block.Block; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Blocks; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.TextComponentTranslation; +import zmaster587.advancedRocketry.api.ARConfiguration; + +import java.util.Collections; +import java.util.List; + +public class AddTorchCommand extends ARCommand { + @Override + public String getName() { + return "addTorch"; + } + + @Override + public List getAliases() { + return Collections.singletonList("addtorch"); + } + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.addtorch.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length != 0) { + throw wrongUsage(sender); + } + EntityPlayerMP player = getCommandSenderAsPlayer(sender); + Block block = Block.getBlockFromItem(player.getHeldItemMainhand().getItem()); + if (block == Blocks.AIR) { + throw new CommandException("commands.advancedrocketry.addtorch.invalid"); + } + if (ARConfiguration.getCurrentConfig().torchBlocks.contains(block)) { + throw new CommandException("commands.advancedrocketry.addtorch.exists", block.getLocalizedName()); + } + ARConfiguration.getCurrentConfig().addTorchblock(block); + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.addtorch.success", block.getLocalizedName())); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/FillDataCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/FillDataCommand.java new file mode 100644 index 000000000..703c459d5 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/FillDataCommand.java @@ -0,0 +1,83 @@ +package zmaster587.advancedRocketry.command.sub; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextComponentTranslation; +import zmaster587.advancedRocketry.api.DataStorage; +import zmaster587.advancedRocketry.item.IDataItem; +import zmaster587.advancedRocketry.item.ItemMultiData; + +import javax.annotation.Nullable; +import java.util.*; + +public class FillDataCommand extends ARCommand { + @Override + public String getName() { + return "fillData"; + } + + @Override + public List getAliases() { + return Arrays.asList("filldata", "fd"); + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.filldata.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length != 2) { + throw wrongUsage(sender); + } + EntityPlayerMP player = getCommandSenderAsPlayer(sender); + ItemStack stack = player.getHeldItem(EnumHand.MAIN_HAND); + if (!stack.isEmpty() && (stack.getItem() instanceof IDataItem || stack.getItem() instanceof ItemMultiData)) { + DataStorage.DataType dataType; + + try { + dataType = DataStorage.DataType.valueOf(args[0].toUpperCase(Locale.ENGLISH)); + } catch (IllegalArgumentException e) { + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.filldata.invalid")); + StringJoiner joiner = new StringJoiner(", "); + Arrays.stream(DataStorage.DataType.values()) + .filter(data -> !data.name().equals("UNDEFINED")) + .map(data -> data.name().toLowerCase()) + .forEach(joiner::add); + sender.sendMessage(new TextComponentString(joiner.toString())); + throw wrongUsage(sender); + } + + int dataAmount = parseInt(args[1]); + + if (stack.getItem() instanceof IDataItem) { + IDataItem item = (IDataItem) stack.getItem(); + item.setData(stack, dataAmount, dataType); + } else if (stack.getItem() instanceof ItemMultiData) { + ItemMultiData item = (ItemMultiData) stack.getItem(); + item.setData(stack, dataAmount, dataType); + } + + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.filldata.success")); + } + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + if (args.length == 1) { + String[] possible = Arrays.stream(DataStorage.DataType.values()) + .filter(data -> !data.name().equals("UNDEFINED")) + .map(data -> data.name().toLowerCase()) + .toArray(String[]::new); + return getListOfStringsMatchingLastWord(args, possible); + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/ReloadRecipesCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/ReloadRecipesCommand.java new file mode 100644 index 000000000..5472799a8 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/ReloadRecipesCommand.java @@ -0,0 +1,58 @@ +package zmaster587.advancedRocketry.command.sub; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextComponentTranslation; +import zmaster587.advancedRocketry.AdvancedRocketry; +import zmaster587.advancedRocketry.integration.CompatibilityMgr; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; + +public class ReloadRecipesCommand extends ARCommand { + @Override + public String getName() { + return "reloadRecipes"; + } + + @Override + public List getAliases() { + return Collections.singletonList("reloadrecipes"); + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.reloadrecipes.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length > 0) { + throw wrongUsage(sender); + } + try { + AdvancedRocketry.machineRecipes.clearAllMachineRecipes(); + AdvancedRocketry.machineRecipes.registerAllMachineRecipes(); + AdvancedRocketry.machineRecipes.createAutoGennedRecipes(AdvancedRocketry.modProducts); + AdvancedRocketry.machineRecipes.registerXMLRecipes(); + + sender.sendMessage(new TextComponentString("Recipes reloaded")); + + CompatibilityMgr.reloadRecipes(); + } catch (Exception e) { + e.printStackTrace(); + ITextComponent message = new TextComponentString(""); + IntStream.range(1, 4) + .boxed() + .map(i -> "commands.advancedrocketry.reloadrecipes.error" + i) + .map(TextComponentTranslation::new) + .forEach(message::appendSibling); + sender.sendMessage(message); + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/SetGravityCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/SetGravityCommand.java new file mode 100644 index 000000000..b1d2a19b6 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/SetGravityCommand.java @@ -0,0 +1,61 @@ +package zmaster587.advancedRocketry.command.sub; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.Entity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import zmaster587.advancedRocketry.api.AdvancedRocketryAPI; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +public class SetGravityCommand extends ARCommand { + @Override + public String getName() { + return "setGravity"; + } + + @Override + public List getAliases() { + return Collections.singletonList("setgravity"); + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.setgravity.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length < 1 || args.length > 2) { + throw wrongUsage(sender); + } + Entity entity; + if (args.length == 2) { + entity = getPlayer(server, sender, args[1]); + } else { + entity = sender.getCommandSenderEntity(); + } + if (entity == null) { + throw wrongUsage(sender); + } + double multiplier = parseDouble(args[0]); + if (multiplier == 0.0D) { + AdvancedRocketryAPI.gravityManager.clearGravityEffect(entity); + } else { + AdvancedRocketryAPI.gravityManager.setGravityMultiplier(entity, multiplier); + } + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + return args.length == 2 ? getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames()) : Collections.emptyList(); + } + + @Override + public boolean isUsernameIndex(String[] args, int index) { + return index == 2; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/dev/DevCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/dev/DevCommand.java new file mode 100644 index 000000000..dd11ab91d --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/dev/DevCommand.java @@ -0,0 +1,24 @@ +package zmaster587.advancedRocketry.command.sub.dev; + +import net.minecraft.command.ICommandSender; +import net.minecraftforge.server.command.CommandTreeBase; +import net.minecraftforge.server.command.CommandTreeHelp; + +public class DevCommand extends CommandTreeBase { + public DevCommand() { + addSubcommand(new DumpBiomesCommand()); + addSubcommand(new RunTestsCommand()); + + addSubcommand(new CommandTreeHelp(this)); + } + + @Override + public String getName() { + return "dev"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.dev.usage"; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/dev/DumpBiomesCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/dev/DumpBiomesCommand.java new file mode 100644 index 000000000..50dc8e151 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/dev/DumpBiomesCommand.java @@ -0,0 +1,60 @@ +package zmaster587.advancedRocketry.command.sub.dev; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.biome.Biome; +import zmaster587.advancedRocketry.command.sub.ARCommand; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; + +public class DumpBiomesCommand extends ARCommand { + @Override + public String getName() { + return "dumpBiomes"; + } + + @Override + public List getAliases() { + return Collections.singletonList("dumpbiomes"); + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.dev.dumpbiomes.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length > 0) { + throw wrongUsage(sender); + } + try { + String fileName = "./BiomeDump.txt"; + Path path = Paths.get(fileName); + if (!Files.exists(path)) { + Files.createFile(path); + } + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.append("ID\tResource name\n"); + for (Biome biome : Biome.REGISTRY) { + writer.append(String.valueOf(Biome.getIdForBiome(biome))) + .append("\t") + .append(biome.getRegistryName().toString()) + .append("\n"); + } + } + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.dev.dumpbiomes.success")); + } catch (IOException e) { + throw new CommandException(e.toString()); + } + } + +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/dev/RunTestsCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/dev/RunTestsCommand.java new file mode 100644 index 000000000..5d7f97a41 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/dev/RunTestsCommand.java @@ -0,0 +1,40 @@ +package zmaster587.advancedRocketry.command.sub.dev; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.common.MinecraftForge; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.unit.IngameTestOrchestrator; + +import java.util.Collections; +import java.util.List; + +public class RunTestsCommand extends ARCommand { + @Override + public String getName() { + return "runTests"; + } + + public List getAliases() { + return Collections.singletonList("runtests"); + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.dev.runtests.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length > 0) { + throw wrongUsage(sender); + } + EntityPlayerMP player = getCommandSenderAsPlayer(sender); + if (!IngameTestOrchestrator.registered) { + MinecraftForge.EVENT_BUS.register(IngameTestOrchestrator.instance); + } + IngameTestOrchestrator.runTests(player.getEntityWorld(), player); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/dev/package-info.java b/src/main/java/zmaster587/advancedRocketry/command/sub/dev/package-info.java new file mode 100644 index 000000000..642a525c5 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/dev/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package zmaster587.advancedRocketry.command.sub.dev; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/package-info.java b/src/main/java/zmaster587/advancedRocketry/command/sub/package-info.java new file mode 100644 index 000000000..d0e5e8f9e --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package zmaster587.advancedRocketry.command.sub; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetCommand.java new file mode 100644 index 000000000..fde64b87a --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetCommand.java @@ -0,0 +1,28 @@ +package zmaster587.advancedRocketry.command.sub.planet; + +import net.minecraft.command.ICommandSender; +import net.minecraftforge.server.command.CommandTreeBase; +import net.minecraftforge.server.command.CommandTreeHelp; + +public class PlanetCommand extends CommandTreeBase { + public PlanetCommand() { + addSubcommand(new PlanetResetCommand()); + addSubcommand(new PlanetListCommand()); + addSubcommand(new PlanetDeleteCommand()); + addSubcommand(new PlanetGenerateCommand()); + addSubcommand(new PlanetSetCommand()); + addSubcommand(new PlanetGetCommand()); + + addSubcommand(new CommandTreeHelp(this)); + } + + @Override + public String getName() { + return "planet"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.planet.usage"; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetDeleteCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetDeleteCommand.java new file mode 100644 index 000000000..f77d7e04a --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetDeleteCommand.java @@ -0,0 +1,50 @@ +package zmaster587.advancedRocketry.command.sub.planet; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.WorldServer; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.network.PacketDimInfo; +import zmaster587.libVulpes.network.PacketHandler; + +public class PlanetDeleteCommand extends ARCommand { + @Override + public String getName() { + return "delete"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.planet.delete.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length != 1) { + throw wrongUsage(sender); + } + int dimId = parseInt(args[0]); + if (!DimensionManager.getInstance().isDimensionCreated(dimId)) { + throw invalidValue("Planet with id", dimId); + } + WorldServer world = net.minecraftforge.common.DimensionManager.getWorld(dimId); + if (world == null || world.playerEntities.isEmpty()) { + DimensionManager.getInstance().deleteDimension(dimId); + PacketHandler.sendToAll(new PacketDimInfo(dimId, null)); + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.planet.delete.success", dimId)); + } else { + //If the world still has players abort and list players + ITextComponent message = new TextComponentTranslation("commands.advancedrocketry.planet.delete.invalid"); + for (EntityPlayer player : world.playerEntities) { + message.appendText("\n"); + message.appendSibling(player.getDisplayName()); + } + sender.sendMessage(message); + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetGenerateCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetGenerateCommand.java new file mode 100644 index 000000000..56c753a5a --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetGenerateCommand.java @@ -0,0 +1,119 @@ +package zmaster587.advancedRocketry.command.sub.planet; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentTranslation; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.dimension.DimensionProperties; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +public class PlanetGenerateCommand extends ARCommand { + @Override + public String getName() { + return "generate"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.planet.generate.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length < 1 || args.length > 10) { + throw wrongUsage(sender); + } + int starId = parseInt(args[0]); + // Offset beginning after the id + int modOffset = 1; + boolean moon = false; + boolean gas = false; + if (args.length > modOffset && args[modOffset].equalsIgnoreCase("moon")) { + modOffset++; + moon = true; + if (!DimensionManager.getInstance().isDimensionCreated(starId)) { + throw invalidValue("Planet with id", starId); + } + } else if (DimensionManager.getInstance().getStar(starId) == null) { + throw invalidValue("Star with id", starId); + } + + if (args.length > modOffset && args[modOffset].equalsIgnoreCase("gas")) { + modOffset++; + gas = true; + } + + // First 3 args are randomness, last 3 args are base value + boolean randArgs = args.length == modOffset + 1 + 3; + boolean fullArgs = args.length == modOffset + 1 + 6; + if (randArgs || fullArgs) { + int planetId = starId; + if (moon) { + starId = DimensionManager.getInstance().getDimensionProperties(planetId).getStarId(); + } + DimensionProperties props; + int argsOffset = modOffset; + if (gas) { + if (randArgs) { + // Defaults are from DimensionManager#generateRandomPlanets() + props = DimensionManager.getInstance().generateRandomGasGiant(starId, args[argsOffset++], + 150, 180, 125, + parseInt(args[argsOffset++]), parseInt(args[argsOffset++]), parseInt(args[argsOffset])); + } else { + // Method params are flipped... + String name = args[argsOffset++]; + int atmosphereFactor = parseInt(args[argsOffset++]); + int distanceFactor = parseInt(args[argsOffset++]); + int gravityFactor = parseInt(args[argsOffset++]); + props = DimensionManager.getInstance().generateRandomGasGiant(starId, name, + parseInt(args[argsOffset++]), parseInt(args[argsOffset++]), parseInt(args[argsOffset]), + atmosphereFactor, distanceFactor, gravityFactor); + } + } else { + if (randArgs) { + props = DimensionManager.getInstance().generateRandom(starId, args[argsOffset++], + parseInt(args[argsOffset++]), parseInt(args[argsOffset++]), parseInt(args[argsOffset])); + } else { + // Method params are flipped... + String name = args[argsOffset++]; + int atmosphereFactor = parseInt(args[argsOffset++]); + int distanceFactor = parseInt(args[argsOffset++]); + int gravityFactor = parseInt(args[argsOffset++]); + props = DimensionManager.getInstance().generateRandom(starId, name, + parseInt(args[argsOffset++]), parseInt(args[argsOffset++]), parseInt(args[argsOffset]), + atmosphereFactor, distanceFactor, gravityFactor); + } + } + if (props == null) { + throw new CommandException("commands.advancedrocketry.planet.generate.invalid", args[modOffset]); + } else { + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.planet.generate.success", args[modOffset])); + } + + // If [moon] specified, the generated dim should be a moon orbiting planetId instead of a planet orbiting starId. + if (moon) { + props.setParentPlanet(DimensionManager.getInstance().getDimensionProperties(planetId)); + DimensionManager.getInstance().getStar(starId).removePlanet(props); + } + } else { + throw wrongUsage(sender); + } + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + if (args.length == 2) { + return getListOfStringsMatchingLastWord(args, "moon", "gas"); + } + if (args.length == 3) { + return Collections.singletonList("gas"); + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetGetCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetGetCommand.java new file mode 100644 index 000000000..1539d519f --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetGetCommand.java @@ -0,0 +1,107 @@ +package zmaster587.advancedRocketry.command.sub.planet; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentTranslation; +import org.apache.commons.lang3.math.NumberUtils; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.dimension.DimensionProperties; + +import javax.annotation.Nullable; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class PlanetGetCommand extends ARCommand { + @Override + public String getName() { + return "get"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.planet.get.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length == 0) { + throw wrongUsage(sender); + } + String propName; + int dimId; + if (NumberUtils.isParsable(args[0])) { + dimId = parseInt(args[0]); + if (args.length < 2) { + throw wrongUsage(sender); + } + propName = args[1]; + } else { + dimId = sender.getEntityWorld().provider.getDimension(); + propName = args[0]; + } + // Validate AR dimension + if (!DimensionManager.getInstance().isDimensionCreated(dimId)) { + throw invalidValue("Dimension", dimId); + } + DimensionProperties props = DimensionManager.getInstance().getDimensionProperties(dimId); + DimensionProperties.PropLookup lookup = new DimensionProperties.PropLookup(props); + String propValue; + try { + MethodHandle propGetter = lookup.getPropertyGetter(propName); + if (propGetter == null) { + throw invalidValue("Field", propName); + } + Object rawPropValue = propGetter.invoke(props); + if (rawPropValue == null) { + propValue = null; + } else if (rawPropValue.getClass().isArray()) { + propValue = Arrays.toString(boxedArray(rawPropValue)); + } else { + propValue = rawPropValue.toString(); + } + } catch (Throwable e) { + if (e instanceof CommandException) { + throw (CommandException) e; + } + e.printStackTrace(); + throw new CommandException("Field lookup failed"); + } + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.planet.get.success", + propName, propValue)); + } + + private Object[] boxedArray(Object array) { + if (array instanceof Object[]) { + return (Object[]) array; + } + int length = Array.getLength(array); + Object[] wrapped = new Object[length]; + for (int i = 0; i < wrapped.length; i++) { + wrapped[i] = Array.get(array, i); + } + return wrapped; + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + int dimId; + if (args.length == 1) { + dimId = sender.getEntityWorld().provider.getDimension(); + } else if (args.length == 2 && NumberUtils.isParsable(args[0])) { + dimId = NumberUtils.toInt(args[0]); + } else { + return Collections.emptyList(); + } + // Validate AR dimension + if (DimensionManager.getInstance().isDimensionCreated(dimId)) { + return getListOfStringsMatchingLastWord(args, DimensionProperties.PropLookup.getPropertyNames(false)); + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetListCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetListCommand.java new file mode 100644 index 000000000..a6db7fb3b --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetListCommand.java @@ -0,0 +1,35 @@ +package zmaster587.advancedRocketry.command.sub.planet; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; + +public class PlanetListCommand extends ARCommand { + @Override + public String getName() { + return "list"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.planet.list.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length > 0) { + throw wrongUsage(sender); + } + ITextComponent message = new TextComponentTranslation("commands.advancedrocketry.planet.list.dimensions"); + for (int i : DimensionManager.getInstance().getRegisteredDimensions()) { + message.appendText("\n"); + message.appendSibling(new TextComponentTranslation("commands.advancedrocketry.planet.list.entry", + i, DimensionManager.getInstance().getDimensionProperties(i).getName())); + } + sender.sendMessage(message); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetResetCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetResetCommand.java new file mode 100644 index 000000000..b1be3c1bf --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetResetCommand.java @@ -0,0 +1,44 @@ +package zmaster587.advancedRocketry.command.sub.planet; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.Entity; +import net.minecraft.server.MinecraftServer; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.network.PacketDimInfo; +import zmaster587.libVulpes.network.PacketHandler; + +public class PlanetResetCommand extends ARCommand { + @Override + public String getName() { + return "reset"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.planet.reset.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length > 1) { + throw wrongUsage(sender); + } + int dimId; + if (args.length == 0) { + Entity entity = sender.getCommandSenderEntity(); + if (entity == null) { + throw wrongUsage(sender); + } + dimId = entity.dimension; + } else { + dimId = parseInt(args[0]); + } + if (!DimensionManager.getInstance().isDimensionCreated(dimId)) { + throw invalidValue("Planet with id", dimId); + } + DimensionManager.getInstance().getDimensionProperties(dimId).resetProperties(); + PacketHandler.sendToAll(new PacketDimInfo(dimId, DimensionManager.getInstance().getDimensionProperties(dimId))); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetSetCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetSetCommand.java new file mode 100644 index 000000000..ed9bb240e --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/PlanetSetCommand.java @@ -0,0 +1,193 @@ +package zmaster587.advancedRocketry.command.sub.planet; + +import com.google.common.primitives.Primitives; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentTranslation; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.math.NumberUtils; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.dimension.DimensionProperties; +import zmaster587.advancedRocketry.network.PacketDimInfo; +import zmaster587.libVulpes.network.PacketHandler; + +import javax.annotation.Nullable; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class PlanetSetCommand extends ARCommand { + @Override + public String getName() { + return "set"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.planet.set.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length == 0) { + throw wrongUsage(sender); + } + String propName; + int dimId; + int argsOffset; + // Parse dimension id + if (NumberUtils.isParsable(args[0])) { + dimId = parseInt(args[0]); + if (args.length < 2) { + throw wrongUsage(sender); + } + propName = args[1]; + argsOffset = 2; + } else { + dimId = sender.getEntityWorld().provider.getDimension(); + propName = args[0]; + argsOffset = 1; + } + // Validate AR dimension + if (!DimensionManager.getInstance().isDimensionCreated(dimId)) { + throw invalidValue("Dimension", dimId); + } + DimensionProperties props = DimensionManager.getInstance().getDimensionProperties(dimId); + if (propName.equalsIgnoreCase("atmosphereDensity")) { + int atmosphereDensity = parseInt(args[argsOffset]); + props.setAtmosphereDensityDirect(atmosphereDensity); + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.planet.set.success", + dimId, propName, atmosphereDensity)); + return; + } + + // Generate property setter + DimensionProperties.PropLookup lookup = new DimensionProperties.PropLookup(props); + MethodHandle propSetter; + try { + propSetter = lookup.getPropertySetter(propName); + } catch (Throwable e) { + e.printStackTrace(); + throw new CommandException("commands.advancedrocketry.planet.set.invalid"); + } + if (propSetter == null) { + throw invalidValue("Field", propName); + } + + MethodType type = propSetter.type(); + Class propType = type.parameterType(type.parameterCount() - 1); + try { + if (propType.isArray()) { + // Parse arg value + MethodHandle propGetter = lookup.getPropertyGetter(propName); + if (propGetter == null) { + throw invalidValue("Field", propName); + } + String[] arrayArgs = Arrays.copyOfRange(args, argsOffset, args.length); + int propArrLength = Array.getLength(propGetter.invoke(props)); + Object[] propValues = parseArrayArgs(arrayArgs, propType, propArrLength, propName); + // Set array property + propSetter.invoke(props, ArrayUtils.toPrimitive(propValues)); + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.planet.set.success", + dimId, propName, Arrays.toString(propValues))); + } else { + // Parse arg value + Object propValue = parseArg(args[argsOffset], propType, propName); + // Set property + propSetter.invoke(props, propValue); + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.planet.set.success", + dimId, propName, propValue)); + } + } catch (Throwable e) { + if (e instanceof CommandException) { + throw (CommandException) e; + } + e.printStackTrace(); + throw new CommandException("commands.advancedrocketry.planet.set.invalid"); + } + PacketHandler.sendToAll(new PacketDimInfo(dimId, props)); + } + + private Object parseArg(String arg, Class propTypeIn, String propName) throws CommandException { + // Parse directly as String + if (propTypeIn.equals(String.class)) { + return arg; + } + Class propType = Primitives.wrap(propTypeIn); + // Parse boolean + if (propType.equals(Boolean.class)) { + Boolean asBool = BooleanUtils.toBooleanObject(arg); + if (asBool != null) { + return asBool; + } + } + // Parse number + else if (Number.class.isAssignableFrom(propType) && NumberUtils.isParsable(arg)) { + return NumberUtils.createNumber(arg); + } + // Property is unsupported type or arg is wrong type + throw new CommandException("commands.advancedrocketry.planet.set.mismatch", + propName, propTypeIn.getSimpleName(), arg); + } + + private Object[] parseArrayArgs(String[] args, Class propTypeIn, int expectedLength, String propName) throws CommandException { + if (args.length != expectedLength) { + throw new CommandException("commands.advancedrocketry.planet.set.wronglength", expectedLength, args.length); + } + // Parse directly as String array + if (propTypeIn.equals(String[].class)) { + return args; + } + Class propType = Primitives.wrap(propTypeIn.getComponentType()); + // Parse boolean + if (propType.equals(Boolean.class)) { + Boolean[] asBools = Arrays.stream(args) + .map(BooleanUtils::toBooleanObject) + .filter(Objects::nonNull) + .toArray(Boolean[]::new); + if (asBools.length == expectedLength) { + return asBools; + } + } + // Parse number + if (Number.class.isAssignableFrom(propType)) { + Object[] asNumbers = Arrays.stream(args) + .filter(NumberUtils::isParsable) + .map(NumberUtils::createNumber) + .filter(propType::isInstance) + .map(propType::cast) + .toArray(length -> (Object[]) Array.newInstance(propType, length)); + if (asNumbers.length == expectedLength) { + return asNumbers; + } + } + // Property is unsupported type or one of the args is wrong type + throw new CommandException("commands.advancedrocketry.planet.set.mismatch", + propName, propTypeIn.getSimpleName(), Arrays.toString(args)); + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + int dimId; + if (args.length == 1) { + dimId = sender.getEntityWorld().provider.getDimension(); + } else if (args.length == 2 && NumberUtils.isParsable(args[0])) { + dimId = NumberUtils.toInt(args[0]); + } else { + return Collections.emptyList(); + } + // Validate AR dimension + if (DimensionManager.getInstance().isDimensionCreated(dimId)) { + return getListOfStringsMatchingLastWord(args, DimensionProperties.PropLookup.getPropertyNames(true)); + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/planet/package-info.java b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/package-info.java new file mode 100644 index 000000000..f464bbb94 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/planet/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package zmaster587.advancedRocketry.command.sub.planet; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/redirect/WeatherCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/redirect/WeatherCommand.java new file mode 100644 index 000000000..da2e5e6fa --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/redirect/WeatherCommand.java @@ -0,0 +1,104 @@ +package zmaster587.advancedRocketry.command.sub.redirect; + +import com.google.common.base.Preconditions; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.WrongUsageException; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.storage.WorldInfo; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionProperties; +import zmaster587.advancedRocketry.world.provider.WorldProviderPlanet; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +public class WeatherCommand extends ARCommand { + @Override + public String getName() { + return "weather"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.weather.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length >= 1 && args.length <= 2) { + int i = (300 + (new Random()).nextInt(600)) * 20; + + if (args.length == 2) { + i = parseInt(args[1], 1, 1000000) * 20; + } + + if (!(sender.getEntityWorld().provider instanceof WorldProviderPlanet)) { + throw new WrongUsageException("commands.advancedrocketry.weather.invalid", sender.getEntityWorld().provider.getDimension()); + } + + World world = sender.getEntityWorld(); + DimensionProperties props = ((WorldProviderPlanet) world.provider).getDimensionProperties(); + Preconditions.checkNotNull(props); + + WorldInfo worldinfo = world.getWorldInfo(); + + if ("clear".equalsIgnoreCase(args[0])) { + // Check if clear weather is allowed + if (props.getRainMarker() == 1 || props.getThunderMarker() == 1) { + notifyCommandListener(sender, this, "commands.weather.always_not_clear"); + return; + } + + worldinfo.setCleanWeatherTime(i); + worldinfo.setRainTime(0); + worldinfo.setThunderTime(0); + worldinfo.setRaining(false); + worldinfo.setThundering(false); + notifyCommandListener(sender, this, "commands.weather.clear"); + } else if ("rain".equalsIgnoreCase(args[0])) { + // Check if raining is allowed + if (props.getRainMarker() == -1) { + notifyCommandListener(sender, this, "commands.weather.cannot_rain"); + return; + } + + worldinfo.setCleanWeatherTime(0); + worldinfo.setRainTime(i); + worldinfo.setThunderTime(i); + worldinfo.setRaining(true); + worldinfo.setThundering(false); + notifyCommandListener(sender, this, "commands.weather.rain"); + } else { + if (!"thunder".equalsIgnoreCase(args[0])) { + throw new WrongUsageException("commands.weather.usage"); + } + // Check if thunder is allowed + if (props.getThunderMarker() == -1) { + notifyCommandListener(sender, this, "commands.weather.cannot_thunder"); + return; + } + + worldinfo.setCleanWeatherTime(0); + worldinfo.setRainTime(i); + worldinfo.setThunderTime(i); + worldinfo.setRaining(true); + worldinfo.setThundering(true); + notifyCommandListener(sender, this, "commands.weather.thunder"); + } + } else { + throw new WrongUsageException("commands.weather.usage"); + } + } + + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + if (args.length == 1) { + return getListOfStringsMatchingLastWord(args, "clear", "rain", "thunder"); + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/redirect/package-info.java b/src/main/java/zmaster587/advancedRocketry/command/sub/redirect/package-info.java new file mode 100644 index 000000000..47b66abab --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/redirect/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package zmaster587.advancedRocketry.command.sub.redirect; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarCommand.java new file mode 100644 index 000000000..b309a774f --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarCommand.java @@ -0,0 +1,107 @@ +package zmaster587.advancedRocketry.command.sub.star; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.WrongUsageException; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraftforge.server.command.CommandTreeBase; +import net.minecraftforge.server.command.CommandTreeHelp; +import zmaster587.advancedRocketry.api.dimension.IDimensionProperties; +import zmaster587.advancedRocketry.api.dimension.solar.StellarBody; + +import javax.annotation.Nullable; + +public class StarCommand extends CommandTreeBase { + public StarCommand() { + addSubcommand(new StarListCommand()); + addSubcommand(new StarGetCommand()); + addSubcommand(new StarSetCommand()); + addSubcommand(new StarGenerateCommand()); + + addSubcommand(new CommandTreeHelp(this)); + } + + @Override + public String getName() { + return "star"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.star.usage"; + } + + enum ActionType { + TEMP("temp") { + @Override + void get(ICommandSender sender, StellarBody star) { + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.star.action.temp.get", star.getTemperature())); + } + + @Override + void set(ICommandSender sender, StellarBody star, String[] args) throws CommandException { + star.setTemperature(parseInt(args[0])); + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.star.action.temp.set", star.getTemperature())); + } + }, + PLANETS("planets") { + @Override + void get(ICommandSender sender, StellarBody star) { + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.star.action.planets.get")); + for (IDimensionProperties planets : star.getPlanets()) { + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.star.action.planets.get.entry", + planets.getId(), planets.getName())); + } + } + + @Override + void set(ICommandSender sender, StellarBody star, String[] args) { + throw new UnsupportedOperationException(); + } + }, + POS("pos") { + @Override + void get(ICommandSender sender, StellarBody star) { + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.star.action.pos.get", + star.getPosX(), star.getPosZ())); + } + + @Override + void set(ICommandSender sender, StellarBody star, String[] args) throws CommandException { + int x = parseInt(args[0]); + int z = parseInt(args[1]); + star.setPosX(x); + star.setPosZ(z); + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.star.action.pos.set", + star.getPosX(), star.getPosZ())); + } + }; + + final String name; + + ActionType(String name) { + this.name = name; + } + + @Nullable + static ActionType byName(String nameIn) + { + for (ActionType actionType : values()) + { + if (actionType.name.equals(nameIn)) + { + return actionType; + } + } + + return null; + } + + WrongUsageException wrongUsageSet() { + return new WrongUsageException("commands.advancedrocketry.star.set." + name + ".usage"); + } + + abstract void get(ICommandSender sender, StellarBody star) throws CommandException; + abstract void set(ICommandSender sender, StellarBody star, String[] args) throws CommandException; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarGenerateCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarGenerateCommand.java new file mode 100644 index 000000000..cc0165262 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarGenerateCommand.java @@ -0,0 +1,47 @@ +package zmaster587.advancedRocketry.command.sub.star; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.TextComponentTranslation; +import zmaster587.advancedRocketry.api.dimension.solar.StellarBody; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.network.PacketStellarInfo; +import zmaster587.libVulpes.network.PacketHandler; + +public class StarGenerateCommand extends ARCommand { + @Override + public String getName() { + return "generate"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.star.generate.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length != 4) { + throw wrongUsage(sender); + } + String name = args[0]; + int temp = parseInt(args[1]); + int x = parseInt(args[2]); + int z = parseInt(args[3]); + StellarBody star = new StellarBody(); + star.setTemperature(temp); + star.setPosX(x); + star.setPosZ(z); + star.setName(name); + star.setId(DimensionManager.getInstance().getNextFreeStarId()); + if (star.getId() != -1) { + DimensionManager.getInstance().addStar(star); + PacketHandler.sendToAll(new PacketStellarInfo(star.getId(), star)); + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.star.generate.success")); + } else { + throw new CommandException("commands.advancedrocketry.star.generate.invalid"); + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarGetCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarGetCommand.java new file mode 100644 index 000000000..4a4156b23 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarGetCommand.java @@ -0,0 +1,51 @@ +package zmaster587.advancedRocketry.command.sub.star; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import zmaster587.advancedRocketry.api.dimension.solar.StellarBody; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.List; + +public class StarGetCommand extends ARCommand { + @Override + public String getName() { + return "get"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.star.get.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length != 2) { + throw wrongUsage(sender); + } + String prop = args[0]; + int starId = parseInt(args[1]); + StellarBody star = DimensionManager.getInstance().getStar(starId); + if (star == null) + throw invalidValue("Star", starId); + else { + StarCommand.ActionType action = StarCommand.ActionType.byName(prop); + if (action != null) { + action.get(sender, star); + } + } + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + String[] possible = Arrays.stream(StarCommand.ActionType.values()) + .map(action -> action.name) + .toArray(String[]::new); + return getListOfStringsMatchingLastWord(args, possible); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarListCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarListCommand.java new file mode 100644 index 000000000..853a9d89c --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarListCommand.java @@ -0,0 +1,29 @@ +package zmaster587.advancedRocketry.command.sub.star; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.TextComponentTranslation; +import zmaster587.advancedRocketry.api.dimension.solar.StellarBody; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; + +public class StarListCommand extends ARCommand { + @Override + public String getName() { + return "list"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.star.list.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + for (StellarBody star : DimensionManager.getInstance().getStars()) { + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.star.list.entry", + star.getId(), star.getName(), star.getNumPlanets())); + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarSetCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarSetCommand.java new file mode 100644 index 000000000..d1ef0aef0 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/star/StarSetCommand.java @@ -0,0 +1,60 @@ +package zmaster587.advancedRocketry.command.sub.star; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import zmaster587.advancedRocketry.api.dimension.solar.StellarBody; +import zmaster587.advancedRocketry.command.ARCommandRoot; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.List; + +public class StarSetCommand extends ARCommand { + @Override + public String getName() { + return "set"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.star.set.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length == 0 || args.length >= 4) { + throw wrongUsage(sender); + } + StarCommand.ActionType action = StarCommand.ActionType.byName(args[0]); + // star set + if (action == null) { + throw wrongUsage(sender); + } else if (args.length == 1) { + throw action.wrongUsageSet(); + } + int starId = parseInt(args[1]); + StellarBody star = DimensionManager.getInstance().getStar(starId); + if (star == null) + throw invalidValue("Star", starId); + else { + String[] propArgs = ARCommandRoot.shiftArgs(args, 2); + if (action == StarCommand.ActionType.TEMP && propArgs.length != 1 + || action == StarCommand.ActionType.POS && propArgs.length != 2) { + throw action.wrongUsageSet(); + } + action.set(sender, star, propArgs); + } + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + String[] possible = Arrays.stream(new StarCommand.ActionType[]{StarCommand.ActionType.TEMP, StarCommand.ActionType.POS}) + .map(action -> action.name) + .toArray(String[]::new); + return getListOfStringsMatchingLastWord(args, possible); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/star/package-info.java b/src/main/java/zmaster587/advancedRocketry/command/sub/star/package-info.java new file mode 100644 index 000000000..9b3ccfd4f --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/star/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package zmaster587.advancedRocketry.command.sub.star; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/station/CreateStationCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/station/CreateStationCommand.java new file mode 100644 index 000000000..55cd34908 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/station/CreateStationCommand.java @@ -0,0 +1,131 @@ +package zmaster587.advancedRocketry.command.sub.station; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.WorldServer; +import zmaster587.advancedRocketry.api.ARConfiguration; +import zmaster587.advancedRocketry.api.AdvancedRocketryItems; +import zmaster587.advancedRocketry.api.Constants; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.dimension.DimensionProperties; +import zmaster587.advancedRocketry.item.ItemStationChip; +import zmaster587.advancedRocketry.stations.SpaceObjectManager; +import zmaster587.advancedRocketry.stations.SpaceStationObject; +import zmaster587.advancedRocketry.world.util.BasicTeleporter; +import zmaster587.libVulpes.util.HashedBlockPosition; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +public class CreateStationCommand extends ARCommand { + @Override + public String getName() { + return "create"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.station.create.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length < 1 || args.length > 3) { + throw wrongUsage(sender); + } + int orbitDimId = parseInt(args[0]); + DimensionProperties props = DimensionManager.getInstance().getDimensionProperties(orbitDimId); + if (orbitDimId != Constants.INVALID_PLANET && + props == DimensionManager.overworldProperties && orbitDimId != props.getId()) { + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.station.create.tip")); + throw new CommandException("commands.advancedrocketry.station.create.invalid", orbitDimId); + } + // Optional player + tp flag parsing + EntityPlayerMP player = null; + int idx = 1; + + if (args.length > idx && !args[idx].equalsIgnoreCase("tp")) { + player = getPlayer(server, sender, args[idx]); + idx++; + } + if (player == null) { + player = getCommandSenderAsPlayer(sender); + } + + boolean teleport = (args.length > idx && args[idx].equalsIgnoreCase("tp")); + // Create + register station + SpaceStationObject station = new SpaceStationObject(); + + // MUST be true BEFORE registerSpaceObject sends PacketSpaceStationInfo + station.beginTransition(0); // created=true + + SpaceObjectManager.getSpaceManager().registerSpaceObject(station, orbitDimId); // now the packet is correct + + int stationId = station.getId(); + HashedBlockPosition spawn = station.getSpawnLocation(); + + // Ensure space world exists + int spaceDim = ARConfiguration.getCurrentConfig().spaceDimId; + if (net.minecraftforge.common.DimensionManager.getWorld(spaceDim) == null) { + net.minecraftforge.common.DimensionManager.initDimension(spaceDim); + } + WorldServer spaceWorld = server.getWorld(spaceDim); + + // Load chunk and build a 3x3 cobble platform under spawn + BlockPos spawnPos = new BlockPos(spawn.x, spawn.y, spawn.z); + spaceWorld.getChunkFromBlockCoords(spawnPos); + + BlockPos base = spawnPos.down(); + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + spaceWorld.setBlockState(base.add(dx, 0, dz), Blocks.COBBLESTONE.getDefaultState(), 2); + } + } + // Ensure the spawn block is clear + spaceWorld.setBlockState(spawnPos, Blocks.AIR.getDefaultState(), net.minecraftforge.common.util.Constants.BlockFlags.DEFAULT); + + // Give a station chip + ItemStack chip = new ItemStack(AdvancedRocketryItems.itemSpaceStationChip); + ItemStationChip.setUUID(chip, stationId); + player.inventory.addItemStackToInventory(chip); + + sender.sendMessage(new TextComponentTranslation("commands.advancedrocketry.station.create.success", + stationId, orbitDimId, spawn.x, spawn.y, spawn.z)); + + // Optional teleport + if (teleport) { + if (player.world.provider.getDimension() != spaceDim) { + player.changeDimension(spaceDim, new BasicTeleporter(spawnPos)); + } else { + player.setPositionAndUpdate(spawn.x + 0.5, spawn.y + 2, spawn.z + 0.5); + } + } + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + if (args.length == 2) { + if ("tp".startsWith(args[1].toLowerCase())) { + return Collections.singletonList("tp"); + } + return getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames()); + } + if (args.length == 3) { + return Collections.singletonList("tp"); + } + return Collections.emptyList(); + } + + @Override + public boolean isUsernameIndex(String[] args, int index) { + return index == 1; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/station/GiveStationCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/station/GiveStationCommand.java new file mode 100644 index 000000000..7ff55c5d9 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/station/GiveStationCommand.java @@ -0,0 +1,54 @@ +package zmaster587.advancedRocketry.command.sub.station; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import zmaster587.advancedRocketry.api.AdvancedRocketryItems; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.item.ItemStationChip; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +public class GiveStationCommand extends ARCommand { + @Override + public String getName() { + return "give"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.station.give.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length < 1 || args.length > 2) { + throw wrongUsage(sender); + } + EntityPlayerMP player; + if (args.length == 2) { + player = getPlayer(server, sender, args[1]); + } else { + player = getCommandSenderAsPlayer(sender); + } + int stationId = parseInt(args[0]); + ItemStack stack = new ItemStack(AdvancedRocketryItems.itemSpaceStationChip); + ItemStationChip.setUUID(stack, stationId); + player.inventory.addItemStackToInventory(stack); + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + return args.length == 2 ? getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames()) : Collections.emptyList(); + } + + @Override + public boolean isUsernameIndex(String[] args, int index) { + return args.length == 2 && index == 2; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/station/StationCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/station/StationCommand.java new file mode 100644 index 000000000..c64452387 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/station/StationCommand.java @@ -0,0 +1,24 @@ +package zmaster587.advancedRocketry.command.sub.station; + +import net.minecraft.command.ICommandSender; +import net.minecraftforge.server.command.CommandTreeBase; +import net.minecraftforge.server.command.CommandTreeHelp; + +public class StationCommand extends CommandTreeBase { + public StationCommand() { + addSubcommand(new CreateStationCommand()); + addSubcommand(new GiveStationCommand()); + + addSubcommand(new CommandTreeHelp(this)); + } + + @Override + public String getName() { + return "station"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.station.usage"; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/station/package-info.java b/src/main/java/zmaster587/advancedRocketry/command/sub/station/package-info.java new file mode 100644 index 000000000..3e2ddc543 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/station/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package zmaster587.advancedRocketry.command.sub.station; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/FetchCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/FetchCommand.java new file mode 100644 index 000000000..3aa31ffca --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/FetchCommand.java @@ -0,0 +1,46 @@ +package zmaster587.advancedRocketry.command.sub.teleport; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.world.util.BasicTeleporter; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +public class FetchCommand extends ARCommand { + @Override + public String getName() { + return "fetch"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.fetch.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length != 1) { + throw wrongUsage(sender); + } + EntityPlayer destPlayer = getCommandSenderAsPlayer(sender); + EntityPlayer otherPlayer = getPlayer(server, sender, args[0]); + + otherPlayer.changeDimension(destPlayer.dimension, new BasicTeleporter(destPlayer.getPosition())); + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { + return args.length == 1 ? getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames()) : Collections.emptyList(); + } + + @Override + public boolean isUsernameIndex(String[] args, int index) { + return index == 1; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/GoToCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/GoToCommand.java new file mode 100644 index 000000000..5bcc34a1b --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/GoToCommand.java @@ -0,0 +1,23 @@ +package zmaster587.advancedRocketry.command.sub.teleport; + +import net.minecraft.command.ICommandSender; +import net.minecraftforge.server.command.CommandTreeBase; +import net.minecraftforge.server.command.CommandTreeHelp; + +public class GoToCommand extends CommandTreeBase { + public GoToCommand() { + addSubcommand(new GoToDimensionCommand()); + addSubcommand(new GoToStationCommand()); + addSubcommand(new CommandTreeHelp(this)); + } + + @Override + public String getName() { + return "goto"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.goto.usage"; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/GoToDimensionCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/GoToDimensionCommand.java new file mode 100644 index 000000000..9cf0f7f61 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/GoToDimensionCommand.java @@ -0,0 +1,46 @@ +package zmaster587.advancedRocketry.command.sub.teleport; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.common.DimensionManager; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.world.util.TeleporterSeekBlock; + +import java.util.Arrays; +import java.util.List; + +public class GoToDimensionCommand extends ARCommand { + @Override + public String getName() { + return "dimension"; + } + + @Override + public List getAliases() { + return Arrays.asList("dim", "d"); + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.goto.dimension.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length != 1) { + throw wrongUsage(sender); + } + EntityPlayerMP player = getCommandSenderAsPlayer(sender); + int dim = parseInt(args[0]); + if (DimensionManager.isDimensionRegistered(dim)) { + if (DimensionManager.getWorld(dim) == null) { + DimensionManager.initDimension(dim); + } + player.changeDimension(dim, new TeleporterSeekBlock(player.getPosition())); + } else { + throw invalidValue(getName(), dim); + } + } +} \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/GoToStationCommand.java b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/GoToStationCommand.java new file mode 100644 index 000000000..8851b852c --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/GoToStationCommand.java @@ -0,0 +1,53 @@ +package zmaster587.advancedRocketry.command.sub.teleport; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import zmaster587.advancedRocketry.api.ARConfiguration; +import zmaster587.advancedRocketry.api.stations.ISpaceObject; +import zmaster587.advancedRocketry.command.sub.ARCommand; +import zmaster587.advancedRocketry.stations.SpaceObjectManager; +import zmaster587.advancedRocketry.world.util.BasicTeleporter; +import zmaster587.libVulpes.util.HashedBlockPosition; + +import java.util.Collections; +import java.util.List; + +public class GoToStationCommand extends ARCommand { + @Override + public String getName() { + return "station"; + } + + @Override + public List getAliases() { + return Collections.singletonList("s"); + } + + @Override + public String getUsage(ICommandSender sender) { + return "commands.advancedrocketry.goto.station.usage"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length != 1) { + throw wrongUsage(sender); + } + EntityPlayerMP player = getCommandSenderAsPlayer(sender); + int dim = ARConfiguration.getCurrentConfig().spaceDimId; + int stationId = parseInt(args[0]); + ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStation(stationId); + + if (spaceObject != null) { + if (player.world.provider.getDimension() != ARConfiguration.getCurrentConfig().spaceDimId) { + player.changeDimension(dim, new BasicTeleporter(player.getPosition())); + } + HashedBlockPosition vec = spaceObject.getSpawnLocation(); + player.setPositionAndUpdate(vec.x, vec.y, vec.z); + } else { + throw invalidValue(getName(), stationId); // station doesnt exist + } + } +} \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/package-info.java b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/package-info.java new file mode 100644 index 000000000..4d6775240 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/command/sub/teleport/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package zmaster587.advancedRocketry.command.sub.teleport; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/common/CommonProxy.java b/src/main/java/zmaster587/advancedRocketry/common/CommonProxy.java index 4fdfd4965..80362f452 100644 --- a/src/main/java/zmaster587/advancedRocketry/common/CommonProxy.java +++ b/src/main/java/zmaster587/advancedRocketry/common/CommonProxy.java @@ -9,9 +9,13 @@ import net.minecraftforge.fml.common.FMLCommonHandler; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.stations.ISpaceObject; +import zmaster587.libVulpes.inventory.modules.ModuleBase; +import zmaster587.libVulpes.inventory.modules.ModuleContainerPanYOnly; import zmaster587.advancedRocketry.network.PacketLaserGun; import zmaster587.advancedRocketry.network.PacketStationUpdate; import zmaster587.libVulpes.network.PacketHandler; +import java.util.List; +import java.util.LinkedList; public class CommonProxy { @@ -25,6 +29,36 @@ public void registerEventHandlers() { } + + public ModuleBase createScrollListPan( + int baseX, int baseY, + List list, + int sizeX, int sizeY + ) { + return new ModuleContainerPanYOnly( + baseX, baseY, + list, new LinkedList<>(), + null, + sizeX - 2, sizeY, + 0, -48, + 0, 72 + ); + } + + /** Generic clear for any UI scroll cache (no-op on server) */ + public void clearScrollCache() { + // no-op on server/common + } + + // Keep existing Observatory API working (optional wrappers) + public ModuleBase createObservatoryAsteroidListPan(int baseX, int baseY, List list2, int sizeX, int sizeY) { + return createScrollListPan(baseX, baseY, list2, sizeX, sizeY); + } + + public void clearObservatoryScrollCache() { + clearScrollCache(); + } + public void spawnParticle(String particle, World world, double x, double y, double z, double motionX, double motionY, double motionZ) { diff --git a/src/main/java/zmaster587/advancedRocketry/dimension/DimensionManager.java b/src/main/java/zmaster587/advancedRocketry/dimension/DimensionManager.java index 34b7f88de..a55fd4172 100644 --- a/src/main/java/zmaster587/advancedRocketry/dimension/DimensionManager.java +++ b/src/main/java/zmaster587/advancedRocketry/dimension/DimensionManager.java @@ -33,12 +33,17 @@ import zmaster587.libVulpes.network.PacketHandler; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; import java.util.Map.Entry; import java.util.zip.GZIPOutputStream; +import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static zmaster587.advancedRocketry.dimension.DimensionProperties.proxylists; @@ -235,7 +240,7 @@ public void setDimProperties(int dimId, DimensionProperties properties) { * @return next free id */ public int getNextFreeDim(int startingValue) { - for (int i = startingValue; i < 10000; i++) { + for (int i = Math.max(startingValue, 2); i < 10000; i++) { if (!net.minecraftforge.common.DimensionManager.isDimensionRegistered(i) && !dimensionList.containsKey(i)) return i; } @@ -495,7 +500,7 @@ public void deleteDimension(int dimId) { dimensionList.remove(dimId); //Delete World Folder - File file = new File(net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory(), workingPath + "/DIM" + dimId); + File file = new File(getCurrentSaveRootDirectory(), workingPath + "/DIM" + dimId); try { FileUtils.deleteDirectory(file); @@ -632,26 +637,34 @@ public void saveDimensions(String filePath) throws Exception { try { File planetXMLOutput = new File(net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory(), filePath + worldXML); - planetXMLOutput.createNewFile(); - File tmpFileXml = File.createTempFile("ARXMLdata_", ".DAT", net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory()); - FileOutputStream bufOutStream = new FileOutputStream(tmpFileXml); - bufOutStream.write(xmlOutput.getBytes()); + // ensure directory exists + File xmlDir = planetXMLOutput.getParentFile(); + if (xmlDir != null) xmlDir.mkdirs(); - //Commit to OS, tell OS to commit to disk, release and close stream - bufOutStream.flush(); - bufOutStream.getFD().sync(); - bufOutStream.close(); + // temp file MUST be in same directory for atomic move to work reliably + File tmpFileXml = new File(xmlDir, planetXMLOutput.getName() + ".tmp"); - //Temp file was written OK, commit - Files.copy(tmpFileXml.toPath(), planetXMLOutput.toPath(), REPLACE_EXISTING); - tmpFileXml.delete(); + try (FileOutputStream bufOutStream = new FileOutputStream(tmpFileXml)) { + bufOutStream.write(xmlOutput.getBytes(StandardCharsets.UTF_8)); + bufOutStream.flush(); + bufOutStream.getFD().sync(); + } + + // commit: atomic swap if supported, fallback to non-atomic move if not supported + try { + Files.move(tmpFileXml.toPath(), planetXMLOutput.toPath(), REPLACE_EXISTING, ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException e) { + Files.move(tmpFileXml.toPath(), planetXMLOutput.toPath(), REPLACE_EXISTING); + } + // best-effort cleanup if something went wrong mid-commit + if (tmpFileXml.exists()) tmpFileXml.delete(); - File file = new File(net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory(), filePath + tempFile); + File file = new File(getCurrentSaveRootDirectory(), filePath + tempFile); file.createNewFile(); //Getting real sick of my planet file getting toasted during debug... - File tmpFile = File.createTempFile("dimprops", ".DAT", net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory()); + File tmpFile = File.createTempFile("dimprops", ".DAT", getCurrentSaveRootDirectory()); FileOutputStream tmpFileOut = new FileOutputStream(tmpFile); DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(new GZIPOutputStream(tmpFileOut))); try { @@ -667,13 +680,13 @@ public void saveDimensions(String filePath) throws Exception { tmpFile.delete(); } catch (Exception e) { - AdvancedRocketry.logger.error("Cannot save advanced rocketry planet file, you may be able to find backups in " + net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory()); + AdvancedRocketry.logger.error("Cannot save advanced rocketry planet file, you may be able to find backups in " + getCurrentSaveRootDirectory()); e.printStackTrace(); } } catch (IOException e) { - AdvancedRocketry.logger.error("Cannot save advanced rocketry planet files, you may be able to find backups in " + net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory()); + AdvancedRocketry.logger.error("Cannot save advanced rocketry planet files, you may be able to find backups in " + getCurrentSaveRootDirectory()); e.printStackTrace(); } } @@ -752,6 +765,18 @@ private List generateRandomPlanets(StellarBody star, int nu return dimPropList; } + @Nullable + private File getCurrentSaveRootDirectory() { + File dir = net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory(); + if (dir == null) { + if (FMLCommonHandler.instance().getMinecraftServerInstance() == null) return null; + + // Server about to start, but worlds haven't loaded yet + return new File(FMLCommonHandler.instance().getSavesDirectory(), FMLCommonHandler.instance().getMinecraftServerInstance().getFolderName()); + } + return dir; + } + public void createAndLoadDimensions(boolean resetFromXml) { //Load planet files //Note: loading this modifies dimOffset @@ -763,7 +788,7 @@ public void createAndLoadDimensions(boolean resetFromXml) { //Check advRocketry folder first File localFile; - localFile = file = new File(net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory() + "/" + DimensionManager.workingPath + "/planetDefs.xml"); + localFile = file = new File(getCurrentSaveRootDirectory() + "/" + DimensionManager.workingPath + "/planetDefs.xml"); logger.info("Checking for config at " + file.getAbsolutePath()); if (!file.exists() || resetFromXml) { //Hi, I'm if check #42, I am true if the config is not in the world/advRocketry folder @@ -1043,7 +1068,7 @@ public Map loadDimensions(String filePath) { FileInputStream inStream; NBTTagCompound nbt; try { - File file = new File(net.minecraftforge.common.DimensionManager.getCurrentSaveRootDirectory(), filePath + tempFile); + File file = new File(getCurrentSaveRootDirectory(), filePath + tempFile); if (!file.exists()) { new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - file.getName().length())).mkdirs(); diff --git a/src/main/java/zmaster587/advancedRocketry/dimension/DimensionProperties.java b/src/main/java/zmaster587/advancedRocketry/dimension/DimensionProperties.java index 4a3f7bfe6..cd4e4ff8e 100644 --- a/src/main/java/zmaster587/advancedRocketry/dimension/DimensionProperties.java +++ b/src/main/java/zmaster587/advancedRocketry/dimension/DimensionProperties.java @@ -40,12 +40,19 @@ import zmaster587.libVulpes.util.VulpineMath; import zmaster587.libVulpes.util.ZUtils; +import javax.annotation.Nullable; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.*; import java.util.Map.Entry; +import java.util.stream.Collectors; public class DimensionProperties implements Cloneable, IDimensionProperties { + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); /** * Contains default graphic {@link ResourceLocation} to display for different planet types */ @@ -57,12 +64,16 @@ public class DimensionProperties implements Cloneable, IDimensionProperties { public static final ResourceLocation planetRingShadow = new ResourceLocation("advancedrocketry:textures/planets/ringShadow.png"); public static final ResourceLocation shadow = new ResourceLocation("advancedrocketry:textures/planets/shadow.png"); public static final ResourceLocation shadow3 = new ResourceLocation("advancedrocketry:textures/planets/shadow3.png"); + public static final int MAX_ATM_PRESSURE = 1600; public static final int MIN_ATM_PRESSURE = 0; public static final int MAX_DISTANCE = Integer.MAX_VALUE; public static final int MIN_DISTANCE = 1; public static final int MAX_GRAVITY = 400; public static final int MIN_GRAVITY = 0; + public static final int WEATHER_START_LENGTH = 168000; + public static final int WEATHER_PROLONGATION_LENGTH = 12000; + //True if dimension is managed and created by AR (false otherwise) public boolean isNativeDimension; public boolean skyRenderOverride; @@ -101,10 +112,11 @@ public class DimensionProperties implements Cloneable, IDimensionProperties { public List requiredArtifacts; // Custom weather properties - public int rainStartLength = 168000; - public int thunderStartLength = 168000; - public int rainProlongationLength = 12000; - public int thunderProlongationLength = 12000; + private boolean customWorldInfo = false; + private int rainStartLength = WEATHER_START_LENGTH; + private int thunderStartLength = WEATHER_START_LENGTH; + private int rainProlongationLength = WEATHER_PROLONGATION_LENGTH; + private int thunderProlongationLength = WEATHER_PROLONGATION_LENGTH; private int rainMarker; // -1 - never rain, 1 - always rain, 0 - regular weather private int thunderMarker; // -1 - never thunder, 1 - always thunder, 0 - regular weather @@ -222,22 +234,6 @@ public DimensionProperties(int id) { //this.chunkMgrTerraformed = new ChunkManagerPlanet(net.minecraftforge.common.DimensionManager.getWorld(id), net.minecraftforge.common.DimensionManager.getWorld(getId()).getWorldInfo().getGeneratorOptions(), getTerraformedBiomes()); } - public int getRainMarker() { - return rainMarker; - } - - public int getThunderMarker() { - return thunderMarker; - } - - public void setRainMarker(int marker) { - this.rainMarker = marker; - } - - public void setThunderMarker(int marker) { - this.thunderMarker = marker; - } - public void load_terraforming_helper(boolean reset) { if (!net.minecraftforge.common.DimensionManager.getWorld(getId()).isRemote) { @@ -1082,7 +1078,9 @@ public void tick() { BlockPos p = i.pos.getBlockPos(); iterator_2.remove(); // Safe removal during iteration World world = (net.minecraftforge.common.DimensionManager.getWorld(getId())); - world.notifyNeighborsOfStateChange(p, world.getBlockState(p).getBlock(), false); + if (world != null) { + world.notifyNeighborsOfStateChange(p, world.getBlockState(p).getBlock(), false); + } } } @@ -1627,12 +1625,28 @@ public void readFromNBT(NBTTagCompound nbt) { volcanoFrequencyMultiplier = nbt.getFloat("volcanoFrequencyMultiplier"); // Custom weather info - rainStartLength = nbt.getInteger("rainStartLength"); - thunderStartLength = nbt.getInteger("thunderStartLength"); - rainProlongationLength = nbt.getInteger("rainProlongationLength"); - thunderProlongationLength = nbt.getInteger("thunderProlongationLength"); - rainMarker = nbt.getInteger("rainMarker"); - thunderMarker = nbt.getInteger("thunderMarker"); + if (nbt.hasKey("rainStartLength", NBT.TAG_INT)) + setRainStartLength(nbt.getInteger("rainStartLength")); + if (nbt.hasKey("thunderStartLength", NBT.TAG_INT)) + setThunderStartLength(nbt.getInteger("thunderStartLength")); + if (nbt.hasKey("rainProlongationLength", NBT.TAG_INT)) + setRainProlongationLength(nbt.getInteger("rainProlongationLength")); + if (nbt.hasKey("thunderProlongationLength", NBT.TAG_INT)) + setThunderProlongationLength(nbt.getInteger("thunderProlongationLength")); + + if (nbt.hasKey("rainMarker", NBT.TAG_INT)) + setRainMarker(nbt.getInteger("rainMarker")); + if (nbt.hasKey("thunderMarker", NBT.TAG_INT)) + setThunderMarker(nbt.getInteger("thunderMarker")); + + // Sanity clamp + if (getRainStartLength() <= 0) setRainStartLength(WEATHER_START_LENGTH); + if (getThunderStartLength() <= 0) setThunderStartLength(WEATHER_START_LENGTH); + if (getRainProlongationLength() <= 0) setRainProlongationLength(WEATHER_PROLONGATION_LENGTH); + if (getThunderProlongationLength() <= 0) setThunderProlongationLength(WEATHER_PROLONGATION_LENGTH); + // Clamp markers to documented range + setRainMarker(MathHelper.clamp(getRainMarker(), -1, 1)); + setThunderMarker(MathHelper.clamp(getThunderMarker(), -1, 1)); //Hierarchy @@ -1952,12 +1966,12 @@ public void writeToNBT(NBTTagCompound nbt) { nbt.setFloat("volcanoFrequencyMultiplier", volcanoFrequencyMultiplier); // Custom weather data - nbt.setInteger("rainStartLength", rainStartLength); - nbt.setInteger("thunderStartLength", thunderStartLength); - nbt.setInteger("rainProlongationLength", rainProlongationLength); - nbt.setInteger("thunderProlongationLength", thunderProlongationLength); - nbt.setInteger("rainMarker", rainMarker); - nbt.setInteger("thunderMarker", thunderMarker); + nbt.setInteger("rainStartLength", getRainStartLength()); + nbt.setInteger("thunderStartLength", getThunderStartLength()); + nbt.setInteger("rainProlongationLength", getRainProlongationLength()); + nbt.setInteger("thunderProlongationLength", getThunderProlongationLength()); + nbt.setInteger("rainMarker", getRainMarker()); + nbt.setInteger("thunderMarker", getThunderMarker()); //Hierarchy if (!childPlanets.isEmpty()) { @@ -2225,6 +2239,81 @@ public float[] getSkyColor() { return skyColor; } + public boolean usesCustomWorldInfo() { + return customWorldInfo; + } + + public void updateCustomWorldInfo() { + boolean isDefault = getRainStartLength() == getThunderStartLength() && getRainStartLength() == WEATHER_START_LENGTH + && getRainProlongationLength() == getThunderProlongationLength() && getRainProlongationLength() == WEATHER_PROLONGATION_LENGTH + && getRainMarker() == 0 && getThunderMarker() == 0; + customWorldInfo = !isDefault; + } + + // + public int getRainStartLength() + { + return rainStartLength; + } + + public void setRainStartLength(int rainStartLength) + { + this.rainStartLength = rainStartLength; + updateCustomWorldInfo(); + } + + public int getThunderStartLength() + { + return thunderStartLength; + } + + public void setThunderStartLength(int thunderStartLength) + { + this.thunderStartLength = thunderStartLength; + updateCustomWorldInfo(); + } + + public int getRainProlongationLength() + { + return rainProlongationLength; + } + + public void setRainProlongationLength(int rainProlongationLength) + { + this.rainProlongationLength = rainProlongationLength; + updateCustomWorldInfo(); + } + + public int getThunderProlongationLength() + { + return thunderProlongationLength; + } + + public void setThunderProlongationLength(int thunderProlongationLength) + { + this.thunderProlongationLength = thunderProlongationLength; + updateCustomWorldInfo(); + } + + public int getRainMarker() { + return rainMarker; + } + + public int getThunderMarker() { + return thunderMarker; + } + + public void setRainMarker(int marker) { + this.rainMarker = marker; + updateCustomWorldInfo(); + } + + public void setThunderMarker(int marker) { + this.thunderMarker = marker; + updateCustomWorldInfo(); + } + // + /** * Temperatures are stored in Kelvin * This facilitates precise temperature calculations and specifications @@ -2361,4 +2450,60 @@ public ResourceLocation getResourceLEO() { return resourceLEO; } } + + /** + * Used to get/set properties by command. + */ + public static class PropLookup { + private final DimensionProperties props; + + public PropLookup(DimensionProperties props) { + this.props = props; + } + + @Nullable + public MethodHandle getPropertyGetter(String name) throws IllegalAccessException { + Optional field = Arrays.stream(props.getClass().getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers())) + .filter(f -> !Modifier.isFinal(f.getModifiers())) + .filter(f -> f.getName().equalsIgnoreCase(name)) + .findFirst(); + if (!field.isPresent()) { + return null; + } + return LOOKUP.unreflectGetter(field.get()); + } + + @Nullable + public MethodHandle getPropertySetter(String name) throws IllegalAccessException { + Optional field = Arrays.stream(props.getClass().getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers())) + .filter(f -> !Modifier.isFinal(f.getModifiers())) + .filter(f -> f.getName().equalsIgnoreCase(name)) + .findFirst(); + if (!field.isPresent()) { + return null; + } + return LOOKUP.unreflectSetter(field.get()); + } + + public static List getPropertyNames(boolean fromSet) { + return Arrays.stream(DimensionProperties.class.getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers())) + .filter(f -> !Modifier.isFinal(f.getModifiers())) + .filter(f -> { + // Only primitives or Strings (and array variants) can be set by command + if (fromSet) { + Class type = f.getType(); + if (type.isArray()) { + type = type.getComponentType(); + } + return type.isPrimitive() || type.equals(String.class); + } + return true; + }) + .map(Field::getName) + .collect(Collectors.toList()); + } + } } diff --git a/src/main/java/zmaster587/advancedRocketry/entity/EntityElevatorCapsule.java b/src/main/java/zmaster587/advancedRocketry/entity/EntityElevatorCapsule.java index f586548ab..088f4c80e 100644 --- a/src/main/java/zmaster587/advancedRocketry/entity/EntityElevatorCapsule.java +++ b/src/main/java/zmaster587/advancedRocketry/entity/EntityElevatorCapsule.java @@ -17,6 +17,7 @@ import net.minecraft.world.WorldServer; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.ITeleporter; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.AdvancedRocketry; @@ -27,7 +28,7 @@ import zmaster587.advancedRocketry.tile.multiblock.TileSpaceElevator; import zmaster587.advancedRocketry.util.DimensionBlockPosition; import zmaster587.advancedRocketry.util.TransitionEntity; -import zmaster587.advancedRocketry.world.util.TeleporterNoPortal; +import zmaster587.advancedRocketry.world.util.BasicTeleporter; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.interfaces.INetworkEntity; import zmaster587.libVulpes.network.PacketEntity; @@ -183,7 +184,7 @@ public Entity changeDimension(int dimensionIn, double posX, double y, double pos WorldServer worldserver1 = minecraftserver.getWorld(dimensionIn); this.setPosition(posX, y, posZ); - Teleporter teleporter = new TeleporterNoPortal(worldserver1); + ITeleporter teleporter = new BasicTeleporter(getPosition()); Entity entity = changeDimension(dimensionIn, teleporter); if (entity == null) diff --git a/src/main/java/zmaster587/advancedRocketry/entity/EntityHoverCraft.java b/src/main/java/zmaster587/advancedRocketry/entity/EntityHoverCraft.java index a9e51fe50..a48d4eb79 100644 --- a/src/main/java/zmaster587/advancedRocketry/entity/EntityHoverCraft.java +++ b/src/main/java/zmaster587/advancedRocketry/entity/EntityHoverCraft.java @@ -12,6 +12,7 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.AdvancedRocketryItems; import zmaster587.advancedRocketry.entity.EntityRocket.PacketType; import zmaster587.libVulpes.interfaces.INetworkEntity; @@ -35,11 +36,21 @@ public class EntityHoverCraft extends Entity implements IInventory, INetworkEnti protected int currentBurnTime; //Used to calculate rendering stuffs protected EmbeddedInventory inv; - private boolean turningLeft, turningRight, turningUp, turningDownforWhat; + private boolean turningUp, turningDownforWhat; + + // Client only, used for interpolation + @SideOnly(Side.CLIENT) + private int lerpSteps; + @SideOnly(Side.CLIENT) + private double lerpX, lerpY, lerpZ; + @SideOnly(Side.CLIENT) + private float lerpYaw, lerpPitch; + public EntityHoverCraft(World par1World) { super(par1World); inv = new EmbeddedInventory(1); setSize(2.5f, 1f); + this.stepHeight = 1.0f; } public EntityHoverCraft(World par1World, double par2, double par4, double par6) { @@ -54,9 +65,35 @@ public EntityHoverCraft(World par1World, double par2, double par4, double par6) this.prevPosX = par2; this.prevPosY = par4; this.prevPosZ = par6; - inv = new EmbeddedInventory(1); + } + @Override + @SideOnly(Side.CLIENT) + public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch, + int posRotationIncrements, boolean teleport) { + + double dx = x - this.posX; + double dy = y - this.posY; + double dz = z - this.posZ; + + // Snap only for large real teleports + if (teleport && (dx*dx + dy*dy + dz*dz) > (16.0 * 16.0)) { + this.setPosition(x, y, z); + this.rotationYaw = this.prevRotationYaw = yaw; + this.rotationPitch = this.prevRotationPitch = pitch; + this.lerpSteps = 0; + return; + } + + this.lerpX = x; + this.lerpY = y; + this.lerpZ = z; + this.lerpYaw = yaw; + this.lerpPitch = pitch; + this.lerpSteps = Math.max(posRotationIncrements, 3); } + + @Override public double getYOffset() { return 0; @@ -147,55 +184,153 @@ public ItemStack getStackInSlot(int i) { public void setInventorySlotContents(int slot, @Nonnull ItemStack itemstack) { inv.setInventorySlotContents(slot, itemstack); } - - public void onTurnRight(boolean state) { - turningRight = state; - PacketHandler.sendToServer(new PacketEntity(this, (byte) EntityRocket.PacketType.TURNUPDATE.ordinal())); + @Override + public Entity getControllingPassenger() { + return this.getPassengers().isEmpty() ? null : this.getPassengers().get(0); } - public void onTurnLeft(boolean state) { - turningLeft = state; - PacketHandler.sendToServer(new PacketEntity(this, (byte) EntityRocket.PacketType.TURNUPDATE.ordinal())); + @Override + public boolean canPassengerSteer() { + return getControllingPassenger() instanceof EntityPlayer; } public void onUp(boolean state) { + if (turningUp == state) return; turningUp = state; - PacketHandler.sendToServer(new PacketEntity(this, (byte) EntityRocket.PacketType.TURNUPDATE.ordinal())); + PacketHandler.sendToServer(new PacketEntity(this, (byte) PacketType.TURNUPDATE.ordinal())); } public void onDown(boolean state) { + if (turningDownforWhat == state) return; turningDownforWhat = state; - PacketHandler.sendToServer(new PacketEntity(this, (byte) EntityRocket.PacketType.TURNUPDATE.ordinal())); + PacketHandler.sendToServer(new PacketEntity(this, (byte) PacketType.TURNUPDATE.ordinal())); } + @Override public void onUpdate() { super.onUpdate(); - if (this.getPassengers().isEmpty()) - this.turningDownforWhat = true; + if (world.isRemote) { + if (isLocallyControlled()) { + // Apply small server correction first (prevents drift/jitter) + clientLerpDriverCorrection(); + Entity ctrl = getControllingPassenger(); + if (ctrl instanceof EntityPlayer) { + tickPhysics((EntityPlayer) ctrl); + } + } else { + clientLerp(); + } + return; + } + + // server authoritative + Entity ctrl = getControllingPassenger(); + if (ctrl instanceof EntityPlayer) { + tickPhysics((EntityPlayer) ctrl); + if (Math.abs(motionX) + Math.abs(motionY) + Math.abs(motionZ) > 1e-5) { + this.velocityChanged = true; + } + } else { + // bleed off motion & still move so state is consistent + this.motionX *= 0.8; + this.motionY *= 0.8; + this.motionZ *= 0.8; + this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ); + if (Math.abs(motionX) + Math.abs(motionY) + Math.abs(motionZ) > 1e-5) { + this.velocityChanged = true; + } + } + } - this.rotationYaw += (turningRight ? 5 : 0) - (turningLeft ? 5 : 0); - double acc = this.getPassengerMovingForward() * MAX_ACCELERATION; - //RCS mode, steer like boat - float yawAngle = (float) (this.rotationYaw * Math.PI / 180f); - this.motionX += acc * MathHelper.sin(-yawAngle); + @SideOnly(Side.CLIENT) + private void clientLerpDriverCorrection() { + if (lerpSteps > 0) { + double dx = lerpX - posX; + double dy = lerpY - posY; + double dz = lerpZ - posZ; + + if (dx*dx + dy*dy + dz*dz < 1e-4) { + lerpSteps = 0; + return; + } + + this.setPosition(posX + dx * 0.2, posY + dy * 0.2, posZ + dz * 0.2); + + float dyaw = MathHelper.wrapDegrees(lerpYaw - rotationYaw); + this.rotationYaw += dyaw * 0.2f; + this.rotationYaw = MathHelper.wrapDegrees(this.rotationYaw); + + this.rotationPitch += (lerpPitch - rotationPitch) * 0.2f; + } + } + + @SideOnly(Side.CLIENT) + private boolean isLocallyControlled() { + Entity ctrl = getControllingPassenger(); + if (!(ctrl instanceof EntityPlayer)) return false; + return net.minecraft.client.Minecraft.getMinecraft().player == ctrl; + } + + @SideOnly(Side.CLIENT) + private void clientLerp() { + if (lerpSteps > 0) { + double nx = posX + (lerpX - posX) / lerpSteps; + double ny = posY + (lerpY - posY) / lerpSteps; + double nz = posZ + (lerpZ - posZ) / lerpSteps; + + float dyaw = MathHelper.wrapDegrees(lerpYaw - rotationYaw); + rotationYaw += dyaw / lerpSteps; + rotationYaw = MathHelper.wrapDegrees(rotationYaw); + + rotationPitch += (lerpPitch - rotationPitch) / lerpSteps; + + lerpSteps--; + setPosition(nx, ny, nz); + } + } + + + private void tickPhysics(EntityPlayer rider) { + // Boat-like turning + throttle + float forward = MathHelper.clamp(rider.moveForward, -1f, 1f); // W/S + float strafe = MathHelper.clamp(rider.moveStrafing, -1f, 1f); // A/D + + this.rotationYaw -= strafe * 4.0f; // turn rate tweak + this.rotationYaw = MathHelper.wrapDegrees(this.rotationYaw); // keep in -180..180 range + + double acc = forward * MAX_ACCELERATION; + float yawRad = (float) Math.toRadians(this.rotationYaw); + + this.motionX += (-acc) * MathHelper.sin(yawRad); + this.motionZ += ( acc) * MathHelper.cos(yawRad); + + // vertical: keep your packet booleans if you want this.motionY += (turningUp ? MAX_ACCELERATION : 0) - (turningDownforWhat ? MAX_ACCELERATION : 0); - this.motionZ += acc * MathHelper.cos(-yawAngle); + + // drag this.motionX *= 0.9; this.motionY *= 0.9; this.motionZ *= 0.9; - if (this.getPosition().getY() > MAX_HEIGHT * 1.1) - this.motionY = 0; - else if (this.getPosition().getY() > MAX_HEIGHT) - this.motionY *= 0.1; - if (this.getRidingEntity() != null) - this.getRidingEntity().fallDistance = 0; - this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ); + // clamps + double h = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ); + if (h > HORIZONTAL_VMAX) { + double s = HORIZONTAL_VMAX / h; + this.motionX *= s; + this.motionZ *= s; + } + this.motionY = MathHelper.clamp(this.motionY, -VERTICAL_VMAX, VERTICAL_VMAX); + + // height cap + if (this.posY > MAX_HEIGHT * 1.1) this.motionY = 0; + else if (this.posY > MAX_HEIGHT) this.motionY *= 0.1; + this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ); } + public float getPassengerMovingForward() { for (Entity entity : this.getPassengers()) { @@ -207,38 +342,32 @@ public float getPassengerMovingForward() { } @Override - public void readDataFromNetwork(ByteBuf in, byte packetId, - NBTTagCompound nbt) { + public void readDataFromNetwork(ByteBuf in, byte packetId, NBTTagCompound nbt) { if (packetId == PacketType.TURNUPDATE.ordinal()) { - nbt.setBoolean("left", in.readBoolean()); - nbt.setBoolean("right", in.readBoolean()); nbt.setBoolean("up", in.readBoolean()); nbt.setBoolean("down", in.readBoolean()); } } + @Override public void writeDataToNetwork(ByteBuf out, byte id) { if (id == PacketType.TURNUPDATE.ordinal()) { - out.writeBoolean(turningLeft); - out.writeBoolean(turningRight); out.writeBoolean(turningUp); out.writeBoolean(turningDownforWhat); } } - @Override - public void useNetworkData(EntityPlayer player, Side side, byte id, - NBTTagCompound nbt) { + @Override + public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompound nbt) { if (id == PacketType.TURNUPDATE.ordinal()) { - this.turningLeft = nbt.getBoolean("left"); - this.turningRight = nbt.getBoolean("right"); this.turningUp = nbt.getBoolean("up"); this.turningDownforWhat = nbt.getBoolean("down"); } } + @Override public boolean isEmpty() { return inv.isEmpty(); diff --git a/src/main/java/zmaster587/advancedRocketry/entity/EntityRocket.java b/src/main/java/zmaster587/advancedRocketry/entity/EntityRocket.java index 96a725916..1f56a004c 100644 --- a/src/main/java/zmaster587/advancedRocketry/entity/EntityRocket.java +++ b/src/main/java/zmaster587/advancedRocketry/entity/EntityRocket.java @@ -25,6 +25,7 @@ import net.minecraft.util.SoundCategory; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.util.text.TextComponentString; @@ -33,12 +34,17 @@ import net.minecraft.world.WorldServer; import net.minecraft.world.chunk.Chunk; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.ForgeChunkManager; +import net.minecraftforge.common.ForgeChunkManager.Ticket; +import net.minecraftforge.common.ForgeChunkManager.Type; +import net.minecraftforge.common.util.ITeleporter; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidTank; import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.oredict.OreDictionary; import zmaster587.advancedRocketry.AdvancedRocketry; import zmaster587.advancedRocketry.advancements.ARAdvancements; import zmaster587.advancedRocketry.api.*; @@ -74,7 +80,7 @@ import zmaster587.advancedRocketry.tile.TileRocketAssemblingMachine; import zmaster587.advancedRocketry.tile.hatch.TileSatelliteHatch; import zmaster587.advancedRocketry.util.*; -import zmaster587.advancedRocketry.world.util.TeleporterNoPortal; +import zmaster587.advancedRocketry.world.util.BasicTeleporter; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.client.util.ProgressBarImage; import zmaster587.libVulpes.gui.CommonResources; @@ -90,6 +96,8 @@ import javax.annotation.Nullable; import java.util.*; + + public class EntityRocket extends EntityRocketBase implements INetworkEntity, IModularInventory, IProgressBar, IButtonInventory, ISelectionNotify, IPlanetDefiner { // set to 2 seconds because keyboard event is not sent to server @@ -124,6 +132,8 @@ public class EntityRocket extends EntityRocketBase implements INetworkEntity, IM protected ModulePlanetSelector container; boolean acceptedPacket = false; SpacePosition spacePosition; + //true if we have posted the landed event after loading from nbt + private transient boolean postedLandedAfterLoad = false; //true if the rocket is on decent private boolean isInOrbit; //True if the rocket isn't on the ground @@ -138,8 +148,23 @@ public class EntityRocket extends EntityRocketBase implements INetworkEntity, IM private int autoDescendTimer; // Is this value even used? //0 to 100, 100 is fully rotated and ready to go, 0 is normal mode private int rcs_mode_counter = 0; - //Used to most of the logic, determining if in RCS mode or not + // Used to most of the logic, determining if in RCS mode or not private boolean rcs_mode = false; + + // Mirror PlanetSelector Progressbars + private DimensionProperties dimCache; + + // Preload ticket for destination chunks on launch event should be enough time to get a warm dimension + private Ticket destPreloadTicket = null; + private int destPreloadDim = Integer.MIN_VALUE; + private long destPreloadExpire = Long.MIN_VALUE; // world time when we auto-release + + // Only show an oxidizer bar when the rocket actually provides oxidizer capacity. + public boolean shouldShowOxBar() { + return getFuelCapacity(FuelRegistry.FuelType.LIQUID_OXIDIZER) > 0; + } + + public EntityRocket(World p_i1582_1_) { super(p_i1582_1_); @@ -177,6 +202,79 @@ public EntityRocket(World world, StorageChunk storage, StatsRocket stats, double landingPadDisplayText.setColor(0x00ff00); } + // PlanetSelector fixing methods + private void selectSystem(int id) { + if (id == Constants.INVALID_PLANET) { + dimCache = null; + } else { + dimCache = DimensionManager.getInstance().getDimensionProperties(id); + } + planetSelectorProgress.setProps(dimCache); + } + + + @Override + public void onSelected(Object sender) { + if (sender instanceof ModulePlanetSelector) { + int id = ((ModulePlanetSelector) sender).getSelectedSystem(); + selectSystem(id); + } + } + @Override + public void onSystemFocusChanged(Object sender) { + if (sender instanceof ModulePlanetSelector) { + int id = ((ModulePlanetSelector) sender).getSelectedSystem(); + selectSystem(id); + } + } + + private void clearPlanetSelectorCache() { + dimCache = null; + planetSelectorProgress.setProps(null); + + // Optional but nice: drop GUI references so nothing keeps stale state + container = null; + } + + private final PlanetSelectorProgressAdapter planetSelectorProgress = new PlanetSelectorProgressAdapter(); + + private static final class PlanetSelectorProgressAdapter implements IProgressBar { + private DimensionProperties props; + + void setProps(DimensionProperties props) { + this.props = props; + } + + @Override + public float getNormallizedProgress(int id) { + int total = getTotalProgress(id); + if (total <= 0) return 0f; + return MathHelper.clamp(getProgress(id) / (float) total, 0f, 1f); + } + + @Override public void setProgress(int id, int progress) {} + @Override public void setTotalProgress(int id, int progress) {} + + @Override + public int getProgress(int id) { + if (props == null) return 0; + // Placeholder style consistent with TilePlanetSelector + if (id == 0 || id == 1 || id == 2) return 25; + return 0; + } + + @Override + public int getTotalProgress(int id) { + if (props == null) return 50; + + if (id == 0) return Math.max(1, props.getAtmosphereDensity() / 16); + if (id == 1) return Math.max(1, props.orbitalDist / 16); + if (id == 2) return Math.max(1, (int)(props.gravitationalMultiplier * 50)); + return 1; + } + } + + /** * @param blockState the blockstate to damage * @return the blockstate that the input blockstate turns into @@ -209,6 +307,50 @@ private static IBlockState getDamagedBlock(IBlockState blockState) { return null; } + private void preloadDestinationChunks(int dimId, double x, double z, int radiusChunks, int holdSeconds) { + if (world.isRemote) return; + + // Clean any previous + releaseDestinationPreload(); + + MinecraftServer server = this.getServer(); + if (server == null) return; + + WorldServer target = server.getWorld(dimId); + if (target == null) return; // dimension not available + + // Request a NORMAL ticket in the DESTINATION world (not bound to this entity) + destPreloadTicket = ForgeChunkManager.requestTicket(AdvancedRocketry.instance, target, Type.NORMAL); + if (destPreloadTicket == null) { + AdvancedRocketry.logger.warn("[EntityRocket] Could not acquire destination preload ticket for dim {}", dimId); + return; + } + + int cx = ((int)Math.floor(x)) >> 4; + int cz = ((int)Math.floor(z)) >> 4; + for (int dx = -radiusChunks; dx <= radiusChunks; dx++) { + for (int dz = -radiusChunks; dz <= radiusChunks; dz++) { + ForgeChunkManager.forceChunk(destPreloadTicket, new ChunkPos(cx + dx, cz + dz)); + } + } + + destPreloadDim = dimId; + // use *server* time base; holdSeconds should be enough to cover ascent (~6s) + destPreloadExpire = world.getTotalWorldTime() + holdSeconds * 20L; + AdvancedRocketry.logger.debug("[EntityRocket] Preloaded 3x3 chunks at dim {} around {},{} for ~{}s", + dimId, (cx<<4), (cz<<4), holdSeconds); + } + + private void releaseDestinationPreload() { + if (destPreloadTicket != null) { + ForgeChunkManager.releaseTicket(destPreloadTicket); + destPreloadTicket = null; + destPreloadDim = Integer.MIN_VALUE; + destPreloadExpire = Long.MIN_VALUE; + } + } + + public void toggleRCS() { if (DimensionManager.getInstance().getDimensionProperties(this.world.provider.getDimension()).isAsteroid()) { rcs_mode = !rcs_mode; @@ -362,11 +504,86 @@ else if (!isInFlight()) return super.getTextOverlay(); } - private void setError(String error) { - this.errorStr = error; + @Nullable + private EntityPlayer getPilot() { + for (Entity e : getPassengers()) { + if (e instanceof EntityPlayer) return (EntityPlayer) e; + } + return null; + } + + @Nonnull + private ItemStack getGateArtifact(@Nullable DimensionProperties destProps) { + if (destProps == null) return ItemStack.EMPTY; + + List req = destProps.getRequiredArtifacts(); + if (req == null || req.isEmpty()) return ItemStack.EMPTY; + + // Contract: always exactly 1 artifact + return req.get(0); + } + + private boolean pilotHasArtifact(@Nullable EntityPlayer pilot, @Nonnull ItemStack req) { + if (pilot == null || req.isEmpty()) return false; + + for (ItemStack have : pilot.inventory.mainInventory) if (matchesRequirement(have, req)) return true; + for (ItemStack have : pilot.inventory.armorInventory) if (matchesRequirement(have, req)) return true; + for (ItemStack have : pilot.inventory.offHandInventory) if (matchesRequirement(have, req)) return true; + + return false; + } + + private boolean matchesRequirement(@Nonnull ItemStack have, @Nonnull ItemStack req) { + if (have.isEmpty()) return false; + if (have.getItem() != req.getItem()) return false; + + // meta / wildcard + int rMeta = req.getItemDamage(); + if (rMeta != OreDictionary.WILDCARD_VALUE && have.getItemDamage() != rMeta) return false; + + // OPTIONAL: require NBT match if your artifact uses NBT (uncomment if needed) + // if (req.hasTagCompound() && !NBTTagCompound.areNBTEquals(req.getTagCompound(), have.getTagCompound())) return false; + + return have.getCount() >= req.getCount(); + } + + + + private static String packReason(String key, Object... args) { + if (args == null || args.length == 0) return key; + + StringBuilder sb = new StringBuilder(key); + for (Object a : args) { + sb.append('|'); + String s = String.valueOf(a); + // Avoid breaking the delimiter if an arg contains '|' + sb.append(s.replace("|", "/")); + } + return sb.toString(); + } + + private void setError(String key, Object... args) { + this.errorStr = key; this.lastErrorTime = this.world.getTotalWorldTime(); + + if (!world.isRemote) { + for (Entity e : this.getPassengers()) { + if (e instanceof EntityPlayerMP) { + ((EntityPlayerMP) e).sendMessage( + new net.minecraft.util.text.TextComponentTranslation(key, args) + ); + } + } + + // send key + args to monitoring station + String packed = packReason(key, args); + MinecraftForge.EVENT_BUS.post(new RocketEvent.RocketAbortEvent(this, packed)); + + this.dataManager.set(LAUNCH_COUNTER, -1); + } } + @Override public void setPosition(double x, double y, double z) { @@ -670,6 +887,45 @@ protected boolean canFitPassenger(Entity passenger) { return this.getPassengers().size() < stats.getNumPassengerSeats(); } + + // Check if we have enough fuel to reach orbit from our current position + private boolean hasMissionFuelFor(int destDimId) { + if (!ARConfiguration.getCurrentConfig().rocketRequireFuel) return true; + + final FuelRegistry.FuelType main = getRocketFuelType(); + if (main == null) return false; // no usable tanks + + if (isInOrbit()) return true; // already at orbit + + if (stats.getThrust() <= stats.getWeight()) return false; + + final DimensionProperties src = DimensionManager.getInstance() + .getDimensionProperties(this.world.provider.getDimension()); + final float gSrc = Math.max(0.01f, src.getGravitationalMultiplier()); + final double a = Math.max(0.0001d, stats.getAcceleration(gSrc)); + final double h = Math.max(0.0, stats.orbitHeight - this.posY); + + long nTicks = (long)Math.ceil(Math.sqrt(2.0 * h / a)); + nTicks += 2L; // small safety buffer + if (nTicks <= 0) nTicks = 1; + + int mainRate = Math.max(1, getFuelConsumptionRate(main)); + long mainNeeded = nTicks * (long)mainRate; + long mainHave = getFuelAmount(main); + if (mainHave < mainNeeded) return false; + + if (main == FuelRegistry.FuelType.LIQUID_BIPROPELLANT) { + int oxRate = Math.max(1, getFuelConsumptionRate(FuelRegistry.FuelType.LIQUID_OXIDIZER)); + long oxNeeded = nTicks * (long)oxRate; + long oxHave = getFuelAmount(FuelRegistry.FuelType.LIQUID_OXIDIZER); + if (oxHave < oxNeeded) return false; + } + + // Descent currently does not burn fuel in your code path. + return true; + } + + /** * @param fluidStack the stack to check whether the rocket can fit * @return boolean on whether said fluid stack can fit into the rocket's internal fuel point storage @@ -690,6 +946,11 @@ public boolean canRocketFitFluid(FluidStack fluidStack) { if (stats.getOxidizerFluid().equals("null") && isCorrectFluid) stats.setOxidizerFluid(fluidStack.getFluid().getName()); return isCorrectFluid; + } else if (FuelRegistry.instance.isFuel(FuelType.NUCLEAR_WORKING_FLUID, fluidStack.getFluid())) { + boolean isCorrectFluid = stats.getWorkingFluid().equals("null") || fluidStack.getFluid() == FluidRegistry.getFluid(stats.getWorkingFluid()); + if (stats.getWorkingFluid().equals("null") && isCorrectFluid) + stats.setWorkingFluid(fluidStack.getFluid().getName()); + return isCorrectFluid; } return false; } @@ -968,7 +1229,15 @@ public void onUpdate() { super.onUpdate(); long deltaTime = world.getTotalWorldTime() - lastWorldTickTicked; lastWorldTickTicked = world.getTotalWorldTime(); - + if (!world.isRemote && !postedLandedAfterLoad && this.ticksExisted >= 5) { + // Consider "landed" = entity exists, NOT in flight, NOT in orbit + if (!isInFlight() && !isInOrbit()) { + net.minecraftforge.common.MinecraftForge.EVENT_BUS.post( + new zmaster587.advancedRocketry.api.RocketEvent.RocketLandedEvent(this) + ); + postedLandedAfterLoad = true; + } + } if (world.isRemote) { double ct = 50; @@ -1084,6 +1353,11 @@ else if (!getRCS() && rcs_mode_counter > 0) { entity.fallDistance = 0; } this.fallDistance = 0; + + // Auto-release destination preload after timeout + if (destPreloadTicket != null && world.getTotalWorldTime() >= destPreloadExpire) { + releaseDestinationPreload(); + } } // When flying around in space @@ -1288,6 +1562,7 @@ else if (distanceSq > this.spacePosition.world.getRenderSizePlanetView() * this. this.motionY = 0; this.setInFlight(false); this.setInOrbit(false); + releaseDestinationPreload(); } @@ -1799,7 +2074,7 @@ public void launch() { destinationDimId = storage.getDestinationDimId(world.provider.getDimension(), (int) this.posX, (int) this.posZ); if (!(DimensionManager.getInstance().canTravelTo(destinationDimId) || (destinationDimId == Constants.INVALID_PLANET && storage.getSatelliteHatches().size() != 0))) { - setError(LibVulpes.proxy.getLocalizedString("error.rocket.cannotGetThere")); + setError("error.rocket.cannotGetThere"); return; } @@ -1814,7 +2089,7 @@ public void launch() { if (spaceObject != null) finalDest = spaceObject.getOrbitingPlanetId(); else { - setError(LibVulpes.proxy.getLocalizedString("error.rocket.destinationNotExist")); + setError("error.rocket.destinationNotExist"); return; } } @@ -1828,30 +2103,99 @@ public void launch() { thisDimId = spaceObject.getProperties().getParentProperties().getId(); } - //Check to see if it's possible to reach - if (finalDest != Constants.INVALID_PLANET && (!stats.isNuclear() || DimensionManager.getInstance().getDimensionProperties(finalDest).getStarId() != DimensionManager.getInstance().getDimensionProperties(thisDimId).getStarId()) && !PlanetaryTravelHelper.isTravelAnywhereInPlanetarySystem(finalDest, thisDimId)) { - setError(LibVulpes.proxy.getLocalizedString("error.rocket.notSameSystem")); - return; + //Check to see if it's possible to reach (split failure modes) + if (finalDest != Constants.INVALID_PLANET) { + + DimensionProperties destProps = DimensionManager.getInstance().getDimensionProperties(finalDest); + DimensionProperties srcProps = DimensionManager.getInstance().getDimensionProperties(thisDimId); + + boolean isNuclear = stats.isNuclear(); + boolean sameStar = destProps.getStarId() == srcProps.getStarId(); + boolean outsidePlanetarySystem = !PlanetaryTravelHelper.isTravelAnywhereInPlanetarySystem(finalDest, thisDimId); + + // Only skip gating for nuclear rockets when config says so + boolean enforceGating = true; + if (isNuclear) { + enforceGating = ARConfiguration.getCurrentConfig().nuclearRocketsRespectArtifactGating; + } + + // Artifact gating: only when arriving from outside the planetary system + ItemStack artifact = getGateArtifact(destProps); + + if (enforceGating && !artifact.isEmpty() && outsidePlanetarySystem) { + EntityPlayer pilot = getPilot(); + if (!pilotHasArtifact(pilot, artifact)) { + setError("error.rocket.gatedArtifactMissingWithItem", + artifact.getCount(), + artifact.getDisplayName()); + return; + } + } + + + + // Nuclear cannot cross stars + if (isNuclear && !sameStar) { + setError("error.rocket.outsideStarSystem"); + return; + } + + // Non-nuclear cannot go outside planetary system + if (!isNuclear && outsidePlanetarySystem) { + setError("error.rocket.outsidePlanetarySystem"); + return; + } } } if (this.stats.getWeight() >= this.stats.getThrust()) { - allowLaunch = false; + setError("error.rocket.tooHeavy"); + return; // hard stop; no silent fall-through } //Check to see what place we should be going to //This is bad but it works and is mostly intelligible so it's here for now stats.orbitHeight = (storage.getGuidanceComputer() == null) ? getEntryHeight(this.world.provider.getDimension()) : storage.getGuidanceComputer().getLaunchSequence(this.world.provider.getDimension(), this.getPosition()); - - + + // Enough fuel for the mission? + if (!hasMissionFuelFor(destinationDimId)) { + setError("error.rocket.notEnoughMissionFuel"); + return; + } + //TODO: Clean this logic a bit? - if (allowLaunch || !stats.hasSeat() || ((DimensionManager.getInstance().isDimensionCreated(destinationDimId)) || destinationDimId == ARConfiguration.getCurrentConfig().spaceDimId || destinationDimId == 0)) { //Abort if destination is invalid + if (allowLaunch || !stats.hasSeat() || ((DimensionManager.getInstance().isDimensionCreated(destinationDimId)) || destinationDimId == ARConfiguration.getCurrentConfig().spaceDimId || destinationDimId == 0)) { setInFlight(true); Iterator connectedTiles = connectedInfrastructure.iterator(); MinecraftForge.EVENT_BUS.post(new RocketLaunchEvent(this)); + // ---- PRELOAD DESTINATION 3x3 (server only) ---- + if (!world.isRemote) { + boolean willTeleportAtAscent = + !(ARConfiguration.getCurrentConfig().experimentalSpaceFlight && storage.getGuidanceComputer().isEmpty()); + + // Only preload when we know we’ll teleport off this world soon + if (willTeleportAtAscent) { + int dimId = destinationDimId; + + boolean canLoad = + DimensionManager.getInstance().isDimensionCreated(dimId) || + dimId == ARConfiguration.getCurrentConfig().spaceDimId; + + if (canLoad) { + Vector3F destVec = (storage != null) ? storage.getDestinationCoordinates(dimId, true) : null; + double dx = (destVec != null) ? destVec.x : this.posX; + double dz = (destVec != null) ? destVec.z : this.posZ; + + preloadDestinationChunks(dimId, dx, dz, /*radiusChunks*/ 1, /*holdSeconds*/ 60); + } + } + } + // ----------------------------------------------- + + //Disconnect things linked to the rocket on liftoff while (connectedTiles.hasNext()) { @@ -1896,6 +2240,7 @@ private void damageGroundBelowRocket(World world, int x, int y, int z, int radiu */ @Override public void deconstructRocket() { + clearPlanetSelectorCache(); super.deconstructRocket(); for (IInfrastructure infrastructure : connectedInfrastructure) { @@ -1910,7 +2255,9 @@ public void deconstructRocket() { @Override public void setDead() { + clearPlanetSelectorCache(); super.setDead(); + releaseDestinationPreload(); if (storage != null && storage.world.displayListIndex != -1) GLAllocation.deleteDisplayLists(storage.world.displayListIndex); @@ -1933,6 +2280,8 @@ public void setOverriddenCoords(int dimId, float x, float y, float z) { @Override public Entity changeDimension(int newDimId) { + clearPlanetSelectorCache(); + return changeDimension(newDimId, this.posX, getEntryHeight(newDimId), this.posZ); } @@ -1954,7 +2303,7 @@ public Entity changeDimension(int dimensionIn, double posX, double y, double pos WorldServer worldserver1 = minecraftserver.getWorld(dimensionIn); this.setPosition(posX, y, posZ); - Teleporter teleporter = new TeleporterNoPortal(worldserver1); + ITeleporter teleporter = new BasicTeleporter(getPosition()); Entity entity = changeDimension(dimensionIn, teleporter); if (entity == null) @@ -1980,6 +2329,7 @@ public void copyDataFromOld(Entity entityIn) { nbttagcompound.removeTag("Passengers"); this.readFromNBT(nbttagcompound); this.timeUntilPortal = entityIn.timeUntilPortal; + clearPlanetSelectorCache(); } protected void readNetworkableNBT(NBTTagCompound nbt) { @@ -2148,9 +2498,13 @@ public void writeDataToNetwork(ByteBuf out, byte id) { if (id == PacketType.RECIEVENBT.ordinal()) { storage.writeToNetwork(out); } else if (id == PacketType.SENDPLANETDATA.ordinal()) { - if (world.isRemote) - out.writeInt(container.getSelectedSystem()); - else { + if (world.isRemote) { + int sel = Constants.INVALID_PLANET; + if (container != null) { + sel = container.getSelectedSystem(); + } + out.writeInt(sel); + } else { if (storage.getGuidanceComputer() != null) { ItemStack stack = storage.getGuidanceComputer().getStackInSlot(0); if (!stack.isEmpty() && stack.getItem() == AdvancedRocketryItems.itemPlanetIdChip) { @@ -2256,6 +2610,7 @@ else if (id == PacketType.RECIEVENBT.ordinal()) { this.turningDownforWhat = nbt.getBoolean("down"); } else if (id == PacketType.ABORTLAUNCH.ordinal()) { this.dataManager.set(LAUNCH_COUNTER, -1); + releaseDestinationPreload(); } else if (id == PacketType.SENDSPACEPOS.ordinal()) { this.spacePosition.readFromNBT(nbt); } else if (id >= STATION_LOC_OFFSET + BUTTON_ID_OFFSET) { @@ -2296,7 +2651,8 @@ private void setDestLandingPad(int padIndex) { } StationLandingLocation location = storage.getGuidanceComputer().getLandingLocation(uuid); - landingPadDisplayText.setText(location != null ? location.toString() : "None Selected"); + String noneLabel = LibVulpes.proxy.getLocalizedString("msg.entity.rocket.none"); + landingPadDisplayText.setText(location != null ? location.toString() : noneLabel); } } @@ -2386,6 +2742,15 @@ public List getModules(int ID, EntityPlayer player) { //Fuel modules.add(new ModuleProgress(192, 7, 0, new ProgressBarImage(2, 173, 12, 71, 17, 6, 3, 69, 1, 1, EnumFacing.UP, TextureResources.rocketHud), this)); + // Conditional oxidizer bar + if (shouldShowOxBar()) { + // Add a second, distinct bar for oxidizer (reuse the monitoring station’s UVs) + modules.add(new ModuleProgress( + 198, 7, 6, // position offset to avoid overlap; ID=6 matches monitoring station semantics + new ProgressBarImage(2, 173, 12, 71, 17, 75, 3, 69, 1, 1, EnumFacing.UP, TextureResources.rocketHud), + this + )); + } //Add buttons @@ -2436,9 +2801,23 @@ public List getModules(int ID, EntityPlayer player) { while (properties.getParentProperties() != null) properties = properties.getParentProperties(); if (stats.isNuclear()) - container = new ModulePlanetSelector(properties.getStarId(), zmaster587.libVulpes.inventory.TextureResources.starryBG, this, this, true); + container = new ModulePlanetSelector( + properties.getStarId(), + zmaster587.libVulpes.inventory.TextureResources.starryBG, + this, // selection notify + planetSelectorProgress, // progress source + this, // planet definer + true + ); else - container = new ModulePlanetSelector(properties.getId(), zmaster587.libVulpes.inventory.TextureResources.starryBG, this, false); + container = new ModulePlanetSelector( + properties.getId(), + zmaster587.libVulpes.inventory.TextureResources.starryBG, + this, // selection notify + planetSelectorProgress, // progress source + false + ); + container.setOffset(1000, 1000); modules.add(container); } @@ -2448,7 +2827,7 @@ public List getModules(int ID, EntityPlayer player) { @Override public String getModularInventoryName() { - return "Rocket"; + return ""; } @Override @@ -2460,14 +2839,23 @@ public float getNormallizedProgress(int id) { case LIQUID_BIPROPELLANT: case LIQUID_MONOPROPELLANT: case NUCLEAR_WORKING_FLUID: - return getFuelAmount(fuelType) / (float) getFuelCapacity(fuelType); + int amt = getFuelAmount(fuelType); + int cap = getFuelCapacity(fuelType); + return (cap > 0) ? (amt / (float) cap) : 0f; } } + // oxidizer bar matches monitoring station’s ID=6 semantics + if (id == 6) { + int oxAmt = getFuelAmount(FuelType.LIQUID_OXIDIZER); + int oxCap = getFuelCapacity(FuelType.LIQUID_OXIDIZER); + return (oxCap > 0) ? (oxAmt / (float) oxCap) : 0f; + } - return 0; + return 0f; } + public double getRelativeHeightFraction() { return (posY - getTopBlock(getPosition()).getY()) / (getEntryHeight(dimension) - getTopBlock(getPosition()).getY()); } @@ -2483,14 +2871,28 @@ public void setProgress(int id, int progress) { @Override public int getProgress(int id) { + if (id == 0) { + FuelType ft = getRocketFuelType(); + return (ft != null) ? getFuelAmount(ft) : 0; + } else if (id == 6) { + return getFuelAmount(FuelType.LIQUID_OXIDIZER); + } return 0; } @Override public int getTotalProgress(int id) { - return 0; + if (id == 0) { + FuelType ft = getRocketFuelType(); + return (ft != null) ? getFuelCapacity(ft) : 1; // never 0 + } else if (id == 6) { + int cap = getFuelCapacity(FuelType.LIQUID_OXIDIZER); + return (cap > 0) ? cap : 1; // never 0 + } + return 1; } + @Override public void setTotalProgress(int id, int progress) { } @@ -2532,21 +2934,13 @@ public StatsRocket getRocketStats() { return stats; } - @Override - public void onSelected(Object sender) { - - } @Override public void onSelectionConfirmed(Object sender) { PacketHandler.sendToServer(new PacketEntity(this, (byte) PacketType.SENDPLANETDATA.ordinal())); } - @Override - public void onSystemFocusChanged(Object sender) { - // TODO Auto-generated method stub - } public LinkedList getConnectedInfrastructure() { return connectedInfrastructure; @@ -2562,6 +2956,9 @@ public boolean isStarKnown(StellarBody body) { return true; } + + + public enum PacketType { RECIEVENBT, SENDINTERACT, diff --git a/src/main/java/zmaster587/advancedRocketry/entity/EntityStationDeployedRocket.java b/src/main/java/zmaster587/advancedRocketry/entity/EntityStationDeployedRocket.java index 228d593d5..831401cb6 100644 --- a/src/main/java/zmaster587/advancedRocketry/entity/EntityStationDeployedRocket.java +++ b/src/main/java/zmaster587/advancedRocketry/entity/EntityStationDeployedRocket.java @@ -26,14 +26,13 @@ import zmaster587.advancedRocketry.api.RocketEvent.RocketLaunchEvent; import zmaster587.advancedRocketry.api.RocketEvent.RocketPreLaunchEvent; import zmaster587.advancedRocketry.api.StatsRocket; -import zmaster587.advancedRocketry.api.atmosphere.AtmosphereRegister; +import zmaster587.advancedRocketry.api.fuel.FuelRegistry; import zmaster587.advancedRocketry.api.stations.ISpaceObject; import zmaster587.advancedRocketry.client.SoundRocketEngine; import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.advancedRocketry.dimension.DimensionProperties; import zmaster587.advancedRocketry.mission.MissionGasCollection; import zmaster587.advancedRocketry.network.PacketSatellite; -import zmaster587.advancedRocketry.network.PacketSatellitesUpdate; import zmaster587.advancedRocketry.stations.SpaceObjectManager; import zmaster587.advancedRocketry.util.AudioRegistry; import zmaster587.advancedRocketry.util.StorageChunk; @@ -62,7 +61,10 @@ public class EntityStationDeployedRocket extends EntityRocket { private ModuleText atmText; private short gasId; private Ticket ticket; - + private long plannedHarvestMb = 0L; // planned total mB to attempt this mission + private transient boolean postedLandedAfterLoad = false; + private transient boolean postedDeorbit = false; + public EntityStationDeployedRocket(World world) { super(world); launchDirection = EnumFacing.DOWN; @@ -121,8 +123,21 @@ public void launch() { setInFlight(true); return; } - if (getFuelAmount(getRocketFuelType()) < getFuelCapacity(getRocketFuelType())) - return; + + if (storage != null) { + storage.recalculateStats(this.stats); // keeps everything else in sync + } + + + FuelRegistry.FuelType rt = getRocketFuelType(); + if (rt != null && ARConfiguration.getCurrentConfig().rocketRequireFuel) { + if (getFuelAmount(rt) < getFuelCapacity(rt)) return; + + if (rt == FuelRegistry.FuelType.LIQUID_BIPROPELLANT) { + if (getFuelAmount(FuelRegistry.FuelType.LIQUID_OXIDIZER) + < getFuelCapacity(FuelRegistry.FuelType.LIQUID_OXIDIZER)) return; + } + } ISpaceObject spaceObj; if (world.provider.getDimension() == ARConfiguration.getCurrentConfig().spaceDimId && @@ -152,7 +167,15 @@ public void launch() { @Override public void onUpdate() { lastWorldTickTicked = world.getTotalWorldTime(); - + if (!world.isRemote && !postedLandedAfterLoad && this.ticksExisted >= 5) { + // Consider "landed" = entity exists, NOT in flight, NOT in orbit + if (!isInFlight() && !isInOrbit()) { + net.minecraftforge.common.MinecraftForge.EVENT_BUS.post( + new zmaster587.advancedRocketry.api.RocketEvent.RocketLandedEvent(this) + ); + postedLandedAfterLoad = true; + } + } if (this.ticksExisted == 20) { //problems with loading on other world then where the infrastructure was set? for (HashedBlockPosition temp : new LinkedList<>(infrastructureCoords)) { @@ -226,6 +249,13 @@ public void onUpdate() { //Returning if (isInOrbit()) { //For unmanned rockets + // Post deorbit once, as we start the return phase + if (!world.isRemote && !postedDeorbit) { + net.minecraftforge.common.MinecraftForge.EVENT_BUS.post( + new zmaster587.advancedRocketry.api.RocketEvent.RocketDeOrbitingEvent(this) + ); + postedDeorbit = true; + } EnumFacing dir; isCoasting = Math.abs(this.posX - actualLaunchLocation.x) < 0.01 && Math.abs(this.posZ - actualLaunchLocation.z) < 0.01; @@ -281,7 +311,12 @@ public void onUpdate() { motionX += acc * forwardDirection.getFrontOffsetX(); motionY += acc * forwardDirection.getFrontOffsetY(); motionZ += acc * forwardDirection.getFrontOffsetZ(); - setFuelAmount(getRocketFuelType(), getFuelAmount(getRocketFuelType()) - 1); + + // server-side fuel consumption for thrust ticks + if (!world.isRemote && burningFuel) { + // only consume if we actually need to (respect config + biprop pairing) + tryConsumeAscentFuel(); + } } if (!world.isRemote && this.getDistance(actualLaunchLocation.x, actualLaunchLocation.y, actualLaunchLocation.z) > 128) { @@ -375,13 +410,15 @@ else if (gasId > props.getHarvestableGasses().size() - 1) /** * Called when the rocket reaches orbit */ + @Override public void onOrbitReached() { - //make it 30 minutes with one drill - - if (world.isRemote)System.out.println("this code should not run on client side!"); + if (world.isRemote) return; // client should not run any of this + // Emit the “reached orbit” event directly so monitors update. + net.minecraftforge.common.MinecraftForge.EVENT_BUS.post( + new zmaster587.advancedRocketry.api.RocketEvent.RocketReachesOrbitEvent(this) + ); + if (this.isDead) return; - if (this.isDead) - return; //Check again to make sure we are around a gas giant ISpaceObject spaceObj; @@ -402,14 +439,64 @@ public void onOrbitReached() { return; } - //one intake with a 1 bucket tank should take 100 seconds - float intakePower = (Integer) stats.getStatTag("intakePower"); + // --- Plan harvest & cap duration by what we can actually get --- + final net.minecraftforge.fluids.Fluid targetFluid = + properties.getHarvestableGasses().get(gasId); + + // (1) config harvest cap (mB) + final boolean infinite = ARConfiguration.getCurrentConfig().gasHarvestInfinite; + final double mult = Math.max(0.0, ARConfiguration.getCurrentConfig().gasHarvestAmountMultiplier); + final long base64k = 64_000L; + final int harvestCapMb = infinite + ? Integer.MAX_VALUE + : (int) Math.min(Integer.MAX_VALUE, Math.round(base64k * mult)); + + // (2) free capacity for this gas across all rocket tanks (simulate) + int freeMb = 0; + for (TileEntity tile : this.storage.getFluidTiles()) { + net.minecraftforge.fluids.capability.IFluidHandler h = + tile.getCapability(net.minecraftforge.fluids.capability.CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); + if (h == null) continue; + int couldTake = h.fill(new net.minecraftforge.fluids.FluidStack(targetFluid, Integer.MAX_VALUE), false); + if (couldTake > 0) { + freeMb = (int) Math.min((long) Integer.MAX_VALUE, (long) freeMb + (long) couldTake); + } + } + + // (3) final planned harvest for this mission + this.plannedHarvestMb = Math.max(0, Math.min(harvestCapMb, freeMb)); + + // (4) duration = min( baseCurveTime(capForTiming), ceil(plannedHarvest / rate) ) + // Keep your curve and denominator 25 + final int liquidCapacity = safeTagInt(stats, "liquidCapacity"); + final int intake = safeTagInt(stats, "intakePower"); + final long rate = DENOM_PER_INTAKE * (long) Math.max(1, intake); // mB/s + + final long durationSeconds; + if (intake <= 0 || this.plannedHarvestMb <= 0) { + durationSeconds = 180L; // safety default + } else { + // IMPORTANT: cap the capacity used by the curve to the harvest cap, + // so durations match the table when harvest is smaller than tank size. + final int capForTiming = infinite ? liquidCapacity : Math.min(liquidCapacity, harvestCapMb); + + double effCapMb = computeEffectiveCapacityMb(capForTiming); + long baseSeconds = (long) Math.floor(effCapMb / (double) rate); + long capSeconds = (long) Math.ceil((double) this.plannedHarvestMb / (double) rate); + + durationSeconds = Math.max(1L, Math.min(baseSeconds, capSeconds)); + } + final long durationTicks = Math.max(1L, durationSeconds * 20L); + + + MissionGasCollection miningMission = + new MissionGasCollection(durationTicks, this, connectedInfrastructure, targetFluid); - MissionGasCollection miningMission = new MissionGasCollection(intakePower == 0 ? 360 : (long) (2 * ((int) stats.getStatTag("liquidCapacity") / intakePower)), this, connectedInfrastructure, properties.getHarvestableGasses().get(gasId)); miningMission.setDimensionId(properties.getId()); properties.addSatellite(miningMission); + // broadcast if (!world.isRemote) { PacketHandler.sendToAll(new PacketSatellite(miningMission)); } @@ -495,8 +582,99 @@ public void writeMissionPersistentNBT(NBTTagCompound nbt) { nbt.setShort("gas", gasId); + nbt.setLong("plannedHarvestMb", Math.max(0L, this.plannedHarvestMb)); + } + + // handle possible bad data gracefully + private static int safeTagInt(StatsRocket s, String key) { + Object v = s.getStatTag(key); + return (v instanceof Number) ? Math.max(0, ((Number) v).intValue()) : 0; + } + + // --- Nonlinear gas mission timing (alpha = 0.2) --- + // effectiveCapacity = BASE_CAP * (liquidCapacity / BASE_CAP)^ALPHA + // baseSeconds = floor( effectiveCapacity / (DENOM_PER_INTAKE * intakePower) ) + // finalSeconds = min(baseSeconds, ceil(plannedHarvestMb / (DENOM_PER_INTAKE * intakePower))) + private static final long BASE_CAP = 64_000L; // 64,000 mB (64 buckets) + private static final double ALPHA = 0.2d; // gentle sublinear scaling + private static final long DENOM_PER_INTAKE = 25L; // you picked "25 * intakePower" + + // Returns the effective capacity (mB) from your nonlinear curve. + private static double computeEffectiveCapacityMb(int liquidCapacity) { + double ratio = Math.max(1.0d, ((double) liquidCapacity) / (double) BASE_CAP); + return (double) BASE_CAP * Math.pow(ratio, ALPHA); + } + + private static long computeMissionDurationSeconds(int liquidCapacity, int intakePower) { + // default fallback if bad data + if (intakePower <= 0) return 180L; // 3 minutes safety default + + // scale in double to avoid precision loss, clamp ratio >= 1 to avoid shrinking below base + double ratio = Math.max(1.0d, ((double) liquidCapacity) / (double) BASE_CAP); + double effectiveCapacity = (double) BASE_CAP * Math.pow(ratio, ALPHA); + + long denom = DENOM_PER_INTAKE * (long) Math.max(1, intakePower); + long secs = (long) Math.floor(effectiveCapacity / (double) denom); + + return Math.max(1L, secs); // never zero + } + + // Consume ascent fuel exactly like the parent rocket does. + // Returns true if fuel was consumed this tick (or fuel is not required by config). + private boolean tryConsumeAscentFuel() { + if (!ARConfiguration.getCurrentConfig().rocketRequireFuel) + return true; + + final FuelRegistry.FuelType rt = getRocketFuelType(); + if (rt == null) + return false; + + // current amounts + int main = getFuelAmount(rt); + final int mainRate = Math.max(1, getFuelConsumptionRate(rt)); // defensive + + if (rt == FuelRegistry.FuelType.LIQUID_BIPROPELLANT) { + int ox = getFuelAmount(FuelRegistry.FuelType.LIQUID_OXIDIZER); + final int oxRate = Math.max(1, getFuelConsumptionRate(FuelRegistry.FuelType.LIQUID_OXIDIZER)); + + // both-or-nothing + if (main >= mainRate && ox >= oxRate) { + setFuelAmount(rt, main - mainRate); + setFuelAmount(FuelRegistry.FuelType.LIQUID_OXIDIZER, ox - oxRate); + } else { + return false; // not enough of one stream + } + + // normalize + clear fluid names when empty + setFuelAmount(rt, Math.max(0, getFuelAmount(rt))); + setFuelAmount(FuelRegistry.FuelType.LIQUID_OXIDIZER, Math.max(0, getFuelAmount(FuelRegistry.FuelType.LIQUID_OXIDIZER))); + + if (getFuelAmount(rt) == 0) { + stats.setFuelFluid("null"); + stats.setWorkingFluid("null"); + } + if (getFuelAmount(FuelRegistry.FuelType.LIQUID_OXIDIZER) == 0) { + stats.setOxidizerFluid("null"); + } + return true; + } else { + if (main >= mainRate) { + setFuelAmount(rt, main - mainRate); + } else { + return false; + } + + // normalize + clear when empty + setFuelAmount(rt, Math.max(0, getFuelAmount(rt))); + if (getFuelAmount(rt) == 0) { + stats.setFuelFluid("null"); + stats.setWorkingFluid("null"); + } + return true; + } } + @Override public void readMissionPersistentNBT(NBTTagCompound nbt) { super.readMissionPersistentNBT(nbt); diff --git a/src/main/java/zmaster587/advancedRocketry/event/PlanetEventHandler.java b/src/main/java/zmaster587/advancedRocketry/event/PlanetEventHandler.java index 29d7cd5e1..9fa57a460 100644 --- a/src/main/java/zmaster587/advancedRocketry/event/PlanetEventHandler.java +++ b/src/main/java/zmaster587/advancedRocketry/event/PlanetEventHandler.java @@ -1,6 +1,5 @@ package zmaster587.advancedRocketry.event; -import net.minecraft.block.BlockLiquid; import net.minecraft.block.BlockTorch; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; @@ -18,14 +17,11 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import net.minecraft.world.World; import net.minecraft.world.WorldProvider; import net.minecraft.world.WorldServer; -import net.minecraft.world.chunk.Chunk; import net.minecraftforge.client.event.EntityViewRenderEvent.FogColors; import net.minecraftforge.client.event.EntityViewRenderEvent.RenderFogEvent; import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; @@ -34,7 +30,6 @@ import net.minecraftforge.event.entity.player.PlayerInteractEvent.RightClickBlock; import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent; import net.minecraftforge.event.terraingen.OreGenEvent; -import net.minecraftforge.event.terraingen.PopulateChunkEvent; import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.BlockEvent.PlaceEvent; import net.minecraftforge.event.world.ChunkEvent; @@ -67,12 +62,10 @@ import zmaster587.advancedRocketry.network.PacketStellarInfo; import zmaster587.advancedRocketry.stations.SpaceObjectManager; import zmaster587.advancedRocketry.stations.SpaceStationObject; -import zmaster587.advancedRocketry.util.BiomeHandler; import zmaster587.advancedRocketry.util.SpawnListEntryNBT; import zmaster587.advancedRocketry.util.TransitionEntity; -import zmaster587.advancedRocketry.world.ChunkManagerPlanet; import zmaster587.advancedRocketry.world.provider.WorldProviderPlanet; -import zmaster587.advancedRocketry.world.util.TeleporterNoPortal; +import zmaster587.advancedRocketry.world.util.BasicTeleporter; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.api.IModularArmor; import zmaster587.libVulpes.network.PacketHandler; @@ -226,7 +219,7 @@ public void playerTick(LivingUpdateEvent event) { event.getEntity().setPositionAndUpdate(teleportPosition.x, teleportPosition.y, teleportPosition.z); } else { event.getEntity().sendMessage(new TextComponentString(LibVulpes.proxy.getLocalizedString("msg.chat.nostation3"))); - event.getEntity().getServer().getPlayerList().transferPlayerToDimension((EntityPlayerMP) event.getEntity(), 0, new TeleporterNoPortal(net.minecraftforge.common.DimensionManager.getWorld(0))); + event.getEntity().changeDimension(0, new BasicTeleporter(event.getEntity().getPosition())); } } @@ -321,7 +314,7 @@ public void tick(TickEvent.ServerTickEvent event) { if (ent.entity.world.getTotalWorldTime() >= ent.time) { ent.entity.setLocationAndAngles(ent.location.getX(), ent.location.getY(), ent.location.getZ(), ent.entity.rotationYaw, ent.entity.rotationPitch); WorldServer newWorld = ent.entity.getServer().getWorld(ent.dimId); - ent.entity.getServer().getPlayerList().transferPlayerToDimension((EntityPlayerMP) ent.entity, ent.dimId, new TeleporterNoPortal(newWorld)); + ent.entity.changeDimension(ent.dimId, new BasicTeleporter(ent.entity.getPosition())); //should be loaded by now Entity rocket = newWorld.getEntityFromUuid(ent.entity2.getPersistentID()); if (rocket != null) diff --git a/src/main/java/zmaster587/advancedRocketry/event/RocketEventHandler.java b/src/main/java/zmaster587/advancedRocketry/event/RocketEventHandler.java index a28d54afe..716da05de 100644 --- a/src/main/java/zmaster587/advancedRocketry/event/RocketEventHandler.java +++ b/src/main/java/zmaster587/advancedRocketry/event/RocketEventHandler.java @@ -13,6 +13,8 @@ import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.settings.GameSettings; import net.minecraft.entity.Entity; import net.minecraft.init.Blocks; import net.minecraft.inventory.EntityEquipmentSlot; @@ -38,6 +40,7 @@ import zmaster587.advancedRocketry.api.RocketEvent; import zmaster587.advancedRocketry.api.armor.IFillableArmor; import zmaster587.advancedRocketry.atmosphere.AtmosphereHandler; +import zmaster587.advancedRocketry.client.KeyBindings; import zmaster587.advancedRocketry.client.render.ClientDynamicTexture; import zmaster587.advancedRocketry.client.render.planet.RenderPlanetarySky; import zmaster587.advancedRocketry.dimension.DimensionManager; @@ -58,6 +61,7 @@ public class RocketEventHandler extends Gui { + private static final int getImgSize = 512; private static final int outerImgSize = getImgSize / 8; private static final int numTicksToDisplay = 100; @@ -141,6 +145,30 @@ public void onScreenRender(RenderGameOverlayEvent.Post event) { vertPos++; } } + // New bottom-right hint + Minecraft mc = Minecraft.getMinecraft(); + if (mc.currentScreen == null) { // no GUI open + FontRenderer fontRenderer = mc.fontRenderer; + String keyName = GameSettings.getKeyDisplayString( + KeyBindings.getOpenRocketUI().getKeyCode() + ); + String hint = I18n.format("msg.entity.rocket.openGuiHint", keyName); + + int scaledW = event.getResolution().getScaledWidth(); + int scaledH = event.getResolution().getScaledHeight(); + int textWidth = fontRenderer.getStringWidth(hint); + int textHeight = fontRenderer.FONT_HEIGHT; + + float scale = 1.0F; + float x = (scaledW - 4 - textWidth * scale) / scale; + float y = (scaledH - 4 - textHeight * scale) / scale; + + GL11.glPushMatrix(); + GL11.glScalef(scale, scale, scale); + fontRenderer.drawStringWithShadow(hint, x, y, 0xFFFFFF); + GL11.glPopMatrix(); + } + } //Draw the O2 Bar if needed diff --git a/src/main/java/zmaster587/advancedRocketry/event/WorldInfoHandler.java b/src/main/java/zmaster587/advancedRocketry/event/WorldInfoHandler.java new file mode 100644 index 000000000..19f2727fe --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/event/WorldInfoHandler.java @@ -0,0 +1,69 @@ +package zmaster587.advancedRocketry.event; + +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import net.minecraft.command.CommandWeather; +import net.minecraft.command.ICommand; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.World; +import net.minecraft.world.storage.DerivedWorldInfo; +import net.minecraft.world.storage.WorldInfo; +import net.minecraftforge.event.CommandEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import zmaster587.advancedRocketry.AdvancedRocketry; +import zmaster587.advancedRocketry.advancedrocketry.Tags; +import zmaster587.advancedRocketry.world.CustomDerivedWorldInfo; +import zmaster587.advancedRocketry.world.provider.WorldProviderPlanet; + +import java.util.Set; +import java.util.StringJoiner; + +public class WorldInfoHandler { + private final Set> commandRedirects = new ReferenceOpenHashSet<>(); + + public WorldInfoHandler() { + commandRedirects.add(CommandWeather.class); + } + + @SubscribeEvent + public void injectCustomWorldInfo(WorldEvent.Load event) { + World world = event.getWorld(); + if (world.isRemote) return; + // Only inject world info into planets generated by AR + if (!(world.provider instanceof WorldProviderPlanet)) return; + + CustomDerivedWorldInfo customInfo = new CustomDerivedWorldInfo(world); + WorldInfo existingInfo = world.getWorldInfo(); + if (existingInfo.getClass() == DerivedWorldInfo.class) { + // Completely override the world info + world.worldInfo = customInfo; + } else if (!(existingInfo instanceof CustomDerivedWorldInfo)) { + // Inject the raw properties into the world info + customInfo.injectWeatherData(existingInfo); + } + } + + @SubscribeEvent + public void redirectCommand(CommandEvent event) { + MinecraftServer server = event.getSender().getServer(); + if (server == null) return; + + Class commandType = event.getCommand().getClass(); + if (!commandRedirects.contains(commandType) + || !(event.getSender().getEntityWorld().provider instanceof WorldProviderPlanet)) { + return; + } + + String command = event.getCommand().getName(); + StringJoiner joiner = new StringJoiner(" "); + joiner.add(Tags.MOD_ID).add(command); + for (String param : event.getParameters()) { + joiner.add(param); + } + String newCommand = joiner.toString(); + + AdvancedRocketry.logger.debug("Redirecting command '/{}' to AR variant '/{}'", command, newCommand); + event.setCanceled(true); + server.getCommandManager().executeCommand(event.getSender(), newCommand); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/CompatibilityMgr.java b/src/main/java/zmaster587/advancedRocketry/integration/CompatibilityMgr.java index 5a7d073d4..9e5cba813 100644 --- a/src/main/java/zmaster587/advancedRocketry/integration/CompatibilityMgr.java +++ b/src/main/java/zmaster587/advancedRocketry/integration/CompatibilityMgr.java @@ -27,4 +27,4 @@ public static void reloadRecipes() { //Hush } } -} +} \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/ARPlugin.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/ARPlugin.java index 22a596f1d..662b54afc 100644 --- a/src/main/java/zmaster587/advancedRocketry/integration/jei/ARPlugin.java +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/ARPlugin.java @@ -7,25 +7,38 @@ import net.minecraft.item.ItemStack; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; import zmaster587.advancedRocketry.api.AdvancedRocketryItems; +import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.block.BlockSmallPlatePress; import zmaster587.advancedRocketry.integration.jei.arcFurnace.ArcFurnaceCategory; import zmaster587.advancedRocketry.integration.jei.arcFurnace.ArcFurnaceRecipeHandler; import zmaster587.advancedRocketry.integration.jei.arcFurnace.ArcFurnaceRecipeMaker; +import zmaster587.advancedRocketry.integration.jei.asteroids.AsteroidCategory; +import zmaster587.advancedRocketry.integration.jei.asteroids.AsteroidRecipeHandler; +import zmaster587.advancedRocketry.integration.jei.asteroids.AsteroidRecipeMaker; import zmaster587.advancedRocketry.integration.jei.centrifuge.CentrifugeCategory; import zmaster587.advancedRocketry.integration.jei.centrifuge.CentrifugeRecipeHandler; import zmaster587.advancedRocketry.integration.jei.centrifuge.CentrifugeRecipeMaker; import zmaster587.advancedRocketry.integration.jei.chemicalReactor.ChemicalReactorCategory; import zmaster587.advancedRocketry.integration.jei.chemicalReactor.ChemicalReactorRecipeHandler; import zmaster587.advancedRocketry.integration.jei.chemicalReactor.ChemicalReactorRecipeMaker; +import zmaster587.advancedRocketry.integration.jei.co2scrubber.Co2ScrubberCategory; +import zmaster587.advancedRocketry.integration.jei.co2scrubber.Co2ScrubberRecipeHandler; +import zmaster587.advancedRocketry.integration.jei.co2scrubber.Co2ScrubberRecipeMaker; import zmaster587.advancedRocketry.integration.jei.crystallizer.CrystallizerCategory; import zmaster587.advancedRocketry.integration.jei.crystallizer.CrystallizerRecipeHandler; import zmaster587.advancedRocketry.integration.jei.crystallizer.CrystallizerRecipeMaker; import zmaster587.advancedRocketry.integration.jei.electrolyser.ElectrolyzerCategory; import zmaster587.advancedRocketry.integration.jei.electrolyser.ElectrolyzerRecipeHandler; import zmaster587.advancedRocketry.integration.jei.electrolyser.ElectrolyzerRecipeMaker; +import zmaster587.advancedRocketry.integration.jei.fuelingStation.FuelingStationCategory; +import zmaster587.advancedRocketry.integration.jei.fuelingStation.FuelingStationRecipeHandler; +import zmaster587.advancedRocketry.integration.jei.fuelingStation.FuelingStationRecipeMaker; import zmaster587.advancedRocketry.integration.jei.lathe.LatheCategory; import zmaster587.advancedRocketry.integration.jei.lathe.LatheRecipeHandler; import zmaster587.advancedRocketry.integration.jei.lathe.LatheRecipeMaker; +import zmaster587.advancedRocketry.integration.jei.orbitalLaserDrill.OrbitalLaserDrillCategory; +import zmaster587.advancedRocketry.integration.jei.orbitalLaserDrill.OrbitalLaserDrillRecipeHandler; +import zmaster587.advancedRocketry.integration.jei.orbitalLaserDrill.OrbitalLaserDrillRecipeMaker; import zmaster587.advancedRocketry.integration.jei.platePresser.PlatePressCategory; import zmaster587.advancedRocketry.integration.jei.platePresser.PlatePressRecipeHandler; import zmaster587.advancedRocketry.integration.jei.platePresser.PlatePressRecipeMaker; @@ -41,7 +54,16 @@ import zmaster587.advancedRocketry.integration.jei.sawmill.SawMillCategory; import zmaster587.advancedRocketry.integration.jei.sawmill.SawMillRecipeHandler; import zmaster587.advancedRocketry.integration.jei.sawmill.SawMillRecipeMaker; +import zmaster587.advancedRocketry.integration.jei.satelliteBuilder.SatelliteBuilderCategory; +import zmaster587.advancedRocketry.integration.jei.satelliteBuilder.SatelliteBuilderRecipeHandler; +import zmaster587.advancedRocketry.integration.jei.satelliteBuilder.SatelliteBuilderRecipeMaker; +import zmaster587.advancedRocketry.integration.jei.stationAssembler.StationAssemblerCategory; +import zmaster587.advancedRocketry.integration.jei.stationAssembler.StationAssemblerRecipeHandler; +import zmaster587.advancedRocketry.integration.jei.stationAssembler.StationAssemblerRecipeMaker; +import zmaster587.advancedRocketry.tile.TileStationAssembler; +import zmaster587.advancedRocketry.tile.infrastructure.TileFuelingStation; import zmaster587.advancedRocketry.tile.multiblock.machine.*; +import zmaster587.advancedRocketry.tile.satellite.TileSatelliteBuilder; import zmaster587.libVulpes.inventory.GuiModular; import javax.annotation.Nonnull; @@ -61,6 +83,12 @@ public class ARPlugin implements IModPlugin { public static final String platePresser = "zmaster587.AR.platePresser"; public static final String centrifugeUUID = "zmaster587.AR.centrifuge"; public static final String precisionLaserEngraverUUID = "zmaster587.AR.precisionlaseretcher"; + public static final String satelliteBuilderUUID = "zmaster587.AR.satelliteBuilder"; + public static final String fuelingStationUUID = "zmaster587.AR.fuelingStation"; + public static final String co2ScrubberUUID = "zmaster587.AR.co2scrubber"; + public static final String stationAssemblerUUID = "zmaster587.AR.stationAssembler"; + public static final String orbitalLaserDrillUUID = "zmaster587.AR.orbitalLaserDrill"; + public static final String asteroidsUUID = "zmaster587.AR.asteroids"; public static IJeiHelpers jeiHelpers; //AR machines can reload recipes. We still need this for JEI to be up-to-date @@ -69,24 +97,44 @@ public static void reload() { jeiHelpers.reload(); } + private static boolean isVoidDrillJeiEnabled() { + ARConfiguration cfg = ARConfiguration.getCurrentConfig(); + return cfg.enableLaserDrill && !cfg.laserDrillPlanet; + } + + @Override public void registerCategories(IRecipeCategoryRegistration registry) { jeiHelpers = registry.getJeiHelpers(); IGuiHelper guiHelper = jeiHelpers.getGuiHelper(); - registry.addRecipeCategories(new RollingMachineCategory(guiHelper), - new LatheCategory(guiHelper), - new PrecisionAssemblerCategory(guiHelper), - new SawMillCategory(guiHelper), - new ChemicalReactorCategory(guiHelper), - new CrystallizerCategory(guiHelper), - new ElectrolyzerCategory(guiHelper), - new ArcFurnaceCategory(guiHelper), - new PlatePressCategory(guiHelper), - new CentrifugeCategory(guiHelper), - new PrecisionLaserEtcherCategory(guiHelper)); + registry.addRecipeCategories( + new RollingMachineCategory(guiHelper), + new LatheCategory(guiHelper), + new PrecisionAssemblerCategory(guiHelper), + new SawMillCategory(guiHelper), + new ChemicalReactorCategory(guiHelper), + new CrystallizerCategory(guiHelper), + new ElectrolyzerCategory(guiHelper), + new ArcFurnaceCategory(guiHelper), + new PlatePressCategory(guiHelper), + new CentrifugeCategory(guiHelper), + new PrecisionLaserEtcherCategory(guiHelper), + new SatelliteBuilderCategory(guiHelper), + new FuelingStationCategory(guiHelper), + new Co2ScrubberCategory(guiHelper), + new StationAssemblerCategory(guiHelper), + new AsteroidCategory(guiHelper) + ); + // ---- Orbital Laser Drill (VoidDrill mode only) ---- + final boolean voidDrillJei = isVoidDrillJeiEnabled(); + if (voidDrillJei) { + registry.addRecipeCategories(new OrbitalLaserDrillCategory(guiHelper)); + } } + + @Override public void register(IModRegistry registry) { @@ -128,7 +176,13 @@ public Object getIngredientUnderMouse(GuiModular guiContainer, new ArcFurnaceRecipeHandler(), new PlatePressRecipeHandler(), new CentrifugeRecipeHandler(), - new PrecisionLaserEtcherRecipeHandler()); + new PrecisionLaserEtcherRecipeHandler(), + new SatelliteBuilderRecipeHandler(), + new FuelingStationRecipeHandler(), + new Co2ScrubberRecipeHandler(), + new StationAssemblerRecipeHandler(), + new AsteroidRecipeHandler() + ); registry.addRecipes(RollingMachineRecipeMaker.getMachineRecipes(jeiHelpers, TileRollingMachine.class), rollingMachineUUID); registry.addRecipes(LatheRecipeMaker.getMachineRecipes(jeiHelpers, TileLathe.class), latheUUID); @@ -141,6 +195,11 @@ public Object getIngredientUnderMouse(GuiModular guiContainer, registry.addRecipes(ChemicalReactorRecipeMaker.getMachineRecipes(jeiHelpers, TileChemicalReactor.class), chemicalReactorUUID); registry.addRecipes(CentrifugeRecipeMaker.getMachineRecipes(jeiHelpers, TileCentrifuge.class), centrifugeUUID); registry.addRecipes(PrecisionLaserEtcherRecipeMaker.getMachineRecipes(jeiHelpers, TilePrecisionLaserEtcher.class), precisionLaserEngraverUUID); + registry.addRecipes(SatelliteBuilderRecipeMaker.getMachineRecipes(jeiHelpers, TileSatelliteBuilder.class), satelliteBuilderUUID); + registry.addRecipes(FuelingStationRecipeMaker.getMachineRecipes(jeiHelpers, TileFuelingStation.class), fuelingStationUUID); + registry.addRecipes(Co2ScrubberRecipeMaker.getRecipes(jeiHelpers), co2ScrubberUUID); + registry.addRecipes(StationAssemblerRecipeMaker.getMachineRecipes(jeiHelpers, TileStationAssembler.class),stationAssemblerUUID); + registry.addRecipes(AsteroidRecipeMaker.getRecipes(jeiHelpers), asteroidsUUID); registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockRollingMachine), rollingMachineUUID); @@ -154,5 +213,32 @@ public Object getIngredientUnderMouse(GuiModular guiContainer, registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockPlatePress), platePresser); registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockCentrifuge), centrifugeUUID); registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockPrecisionLaserEngraver), precisionLaserEngraverUUID); + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockSatelliteBuilder), satelliteBuilderUUID); + // Station Assembler catalyst + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockStationBuilder), stationAssemblerUUID); + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryItems.itemSpaceStationChip), stationAssemblerUUID); + // Co2 Scrubber catalysts + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockCO2Scrubber), co2ScrubberUUID); + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockOxygenVent), co2ScrubberUUID); + + // One tab: Fueling Station + Tank-type catalysts (mono / biprop fuel / oxidizer / working fluid) + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockFuelingStation), fuelingStationUUID); + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockFuelTank), fuelingStationUUID); // mono + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockBipropellantFuelTank), fuelingStationUUID); // biprop fuel + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockOxidizerFuelTank), fuelingStationUUID); // oxidizer + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockNuclearFuelTank), fuelingStationUUID); // working fluid + + // Asteroids (catalyst): observatory and asteroid chip are what players associate with this system + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockObservatory), asteroidsUUID); + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryItems.itemAsteroidChip), asteroidsUUID); + + // ---- Orbital Laser Drill (VoidDrill mode only) ---- + // Voiddrill means laserdrillPlanet is false + final boolean voidDrillJei = isVoidDrillJeiEnabled(); + if (voidDrillJei) { + registry.addRecipeHandlers(new OrbitalLaserDrillRecipeHandler()); + registry.addRecipes(OrbitalLaserDrillRecipeMaker.getRecipes(jeiHelpers), orbitalLaserDrillUUID); + registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockSpaceLaser), orbitalLaserDrillUUID); + } } } diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/MachineRecipe.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/MachineRecipe.java index 927b939f8..00df285e1 100644 --- a/src/main/java/zmaster587/advancedRocketry/integration/jei/MachineRecipe.java +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/MachineRecipe.java @@ -4,6 +4,7 @@ import mezz.jei.api.recipe.IRecipeWrapper; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import zmaster587.libVulpes.interfaces.IRecipe; @@ -81,14 +82,33 @@ public int getTime() { @Override public void drawInfo(Minecraft minecraft, int recipeWidth, - int recipeHeight, int mouseX, int mouseY) { + int recipeHeight, int mouseX, int mouseY) { - String powerString = String.format("Power: %d RF/t", energy); FontRenderer fontRendererObj = minecraft.fontRenderer; + + // Localized labels + String powerLabel = I18n.format("jei.machinerecipe.power"); + String timeLabel = I18n.format("jei.machinerecipe.time"); + + String powerString = String.format("%s %d RF/t", powerLabel, energy); fontRendererObj.drawString(powerString, 0, 55, Color.black.getRGB()); - String timeString = String.format("Time: %d s", time / 20); - fontRendererObj.drawString(timeString, recipeWidth - 55, 55, Color.black.getRGB()); + // --- Time formatting: ticks if below 1 second, otherwise seconds --- + final int ticksPerSecond = 20; + + String timeValue; + if (time < ticksPerSecond) { + // 1..19 ticks + timeValue = String.format("%d ticks", time); + } else { + // 20 ticks -> 1 s, 40 ticks -> 2 s, etc. + timeValue = String.format("%d s", time / ticksPerSecond); + } + + String timeString = String.format("%s %s", timeLabel, timeValue); + // Right-align the time string + int x = recipeWidth - fontRendererObj.getStringWidth(timeString); + fontRendererObj.drawString(timeString, x, 55, Color.black.getRGB()); } } diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidCategory.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidCategory.java new file mode 100644 index 000000000..061feffa2 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidCategory.java @@ -0,0 +1,109 @@ +package zmaster587.advancedRocketry.integration.jei.asteroids; + +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IGuiItemStackGroup; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeCategory; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; +import zmaster587.libVulpes.LibVulpes; + +public class AsteroidCategory implements IRecipeCategory { + + private final IDrawable background; + private final IDrawable icon; + private final IDrawable slotFrame; + + private static final int SLOT = 18; + private static final int PAD = 6; + private static final int GAP = 10; + private static final int TITLE_H = 12; + + // Background: [inputs col] gap [6x2 grid] + private static final int BG_W = PAD + SLOT + GAP + (AsteroidWrapper.COLS * SLOT) + PAD; + private static final int BG_H = TITLE_H + PAD + (AsteroidWrapper.ROWS * SLOT) + PAD; + + // Grid top-left + private static final int GRID_X0 = PAD + SLOT + GAP; + private static final int GRID_Y0 = TITLE_H + PAD; + + // Inputs centered vs grid + private static final int IN_X = PAD; + private static final int IN_GAP = 0; + private static final int IN0_Y = GRID_Y0 + (AsteroidWrapper.ROWS * SLOT - (2 * SLOT + IN_GAP)) / 2; + private static final int IN1_Y = IN0_Y + SLOT + IN_GAP; + + public AsteroidCategory(IGuiHelper gui) { + this.background = gui.createBlankDrawable(BG_W, BG_H); + this.icon = gui.createDrawableIngredient(new ItemStack(AdvancedRocketryBlocks.blockObservatory)); + this.slotFrame = gui.getSlotDrawable(); + } + + @Override public String getUid() { return ARPlugin.asteroidsUUID; } + @Override public String getTitle() { return LibVulpes.proxy.getLocalizedString("jei.ar.asteroids"); } + @Override public String getModName() { return "Advanced Rocketry"; } + @Override public IDrawable getBackground() { return background; } + @Override public IDrawable getIcon() { return icon; } + + @Override + public void setRecipe(IRecipeLayout layout, AsteroidWrapper wrapper, IIngredients ing) { + IGuiItemStackGroup items = layout.getItemStacks(); + + // Inputs + items.init(0, true, IN_X, IN0_Y); + items.init(1, true, IN_X, IN1_Y); + + // Outputs: 6x2 = 12 + int slot = 2; + for (int row = 0; row < AsteroidWrapper.ROWS; row++) { + for (int col = 0; col < AsteroidWrapper.COLS; col++) { + items.init(slot, false, GRID_X0 + col * SLOT, GRID_Y0 + row * SLOT); + slot++; + } + } + + // Bind inputs + java.util.List> inLists = + ing.getInputs(mezz.jei.api.ingredients.VanillaTypes.ITEM); + if (inLists.size() > 0) items.set(0, inLists.get(0)); + if (inLists.size() > 1) items.set(1, inLists.get(1)); + + // Bind outputs + java.util.List> outLists = + ing.getOutputs(mezz.jei.api.ingredients.VanillaTypes.ITEM); + if (!outLists.isEmpty() && !outLists.get(0).isEmpty()) { + java.util.List outs = outLists.get(0); + for (int i = 0; i < AsteroidWrapper.PAGE_SIZE; i++) { + int jeiSlot = 2 + i; + if (i < outs.size()) items.set(jeiSlot, outs.get(i)); + } + } + } + + @Override + public void drawExtras(Minecraft mc) { + // Reset GL so slot drawable is vanilla-grey (no tint) + GlStateManager.color(1f, 1f, 1f, 1f); + GlStateManager.disableLighting(); + GlStateManager.enableAlpha(); + GlStateManager.enableBlend(); + + // Input frames + slotFrame.draw(mc, IN_X, IN0_Y); + slotFrame.draw(mc, IN_X, IN1_Y); + + // Grid frames + for (int row = 0; row < AsteroidWrapper.ROWS; row++) { + for (int col = 0; col < AsteroidWrapper.COLS; col++) { + slotFrame.draw(mc, GRID_X0 + col * SLOT, GRID_Y0 + row * SLOT); + } + } + + GlStateManager.color(1f, 1f, 1f, 1f); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidRecipeHandler.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidRecipeHandler.java new file mode 100644 index 000000000..925756e18 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidRecipeHandler.java @@ -0,0 +1,12 @@ +package zmaster587.advancedRocketry.integration.jei.asteroids; + +import mezz.jei.api.recipe.IRecipeHandler; +import mezz.jei.api.recipe.IRecipeWrapper; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; + +public class AsteroidRecipeHandler implements IRecipeHandler { + @Override public Class getRecipeClass() { return AsteroidWrapper.class; } + @Override public String getRecipeCategoryUid(AsteroidWrapper r) { return ARPlugin.asteroidsUUID; } + @Override public IRecipeWrapper getRecipeWrapper(AsteroidWrapper r) { return r; } + @Override public boolean isRecipeValid(AsteroidWrapper r) { return r != null && r.isValid(); } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidRecipeMaker.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidRecipeMaker.java new file mode 100644 index 000000000..9e1281346 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidRecipeMaker.java @@ -0,0 +1,143 @@ +package zmaster587.advancedRocketry.integration.jei.asteroids; + +import mezz.jei.api.IJeiHelpers; +import net.minecraftforge.fml.common.Loader; +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.AdvancedRocketry; +import zmaster587.advancedRocketry.api.ARConfiguration; +import zmaster587.advancedRocketry.util.Asteroid; +import zmaster587.advancedRocketry.util.XMLAsteroidLoader; + +import java.io.File; +import java.util.*; + +public class AsteroidRecipeMaker { + + private static List cached = null; + private static long cachedMTime = -1L; + + public static List getRecipes(IJeiHelpers helpers) { + // Primary truth: XML in config folder (avoids load-order race) + File xml = getAsteroidXmlFile(); + long mtime = (xml != null && xml.exists()) ? xml.lastModified() : -1L; + + if (cached != null && mtime == cachedMTime) { + return cached; + } + + List fromXml = tryLoadFromXml(xml); + if (fromXml != null && !fromXml.isEmpty()) { + cached = fromXml; + cachedMTime = mtime; + return cached; + } + + // Fallback: whatever AR already has in memory (better than nothing) + try { + Map map = ARConfiguration.getCurrentConfig().asteroidTypes; + if (map != null && !map.isEmpty()) { + cached = buildPagedFromMap(map); + cachedMTime = mtime; + return cached; + } + } catch (Throwable ignored) {} + + cached = Collections.emptyList(); + cachedMTime = mtime; + return cached; + } + + private static File getAsteroidXmlFile() { + try { + // Most robust in modded: use Forge config dir + File cfgDir = Loader.instance().getConfigDir(); + String folder = ARConfiguration.configFolder; // AR uses this folder name + return new File(cfgDir, folder + "/asteroidConfig.xml"); + } catch (Throwable t) { + // Fallback to run-dir relative path + try { + String folder = ARConfiguration.configFolder; + return new File("./config/" + folder + "/asteroidConfig.xml"); + } catch (Throwable ignored) { + return null; + } + } + } + + private static List tryLoadFromXml(File file) { + try { + if (file == null || !file.exists()) { + AdvancedRocketry.logger.warn("[JEI] asteroidConfig.xml not found: " + (file != null ? file.getAbsolutePath() : "null")); + return Collections.emptyList(); + } + + XMLAsteroidLoader loader = new XMLAsteroidLoader(); + if (!loader.loadFile(file)) { + AdvancedRocketry.logger.warn("[JEI] Failed parsing asteroidConfig.xml: " + file.getAbsolutePath()); + return Collections.emptyList(); + } + + List asteroids = loader.loadPropertyFile(); + if (asteroids == null || asteroids.isEmpty()) { + AdvancedRocketry.logger.warn("[JEI] asteroidConfig.xml parsed but produced 0 asteroids"); + return Collections.emptyList(); + } + + // Convert list -> map-like key usage + Map map = new LinkedHashMap<>(); + for (Asteroid a : asteroids) { + if (a == null) continue; + String key = (a.ID != null && !a.ID.isEmpty()) ? a.ID : a.getName(); + if (key == null || key.isEmpty()) key = "asteroid"; + map.put(key, a); + } + + List out = buildPagedFromMap(map); + AdvancedRocketry.logger.info("[JEI] Loaded " + out.size() + " asteroid JEI recipes from XML"); + return out; + + } catch (Throwable t) { + AdvancedRocketry.logger.warn("[JEI] Exception loading asteroids for JEI", t); + return Collections.emptyList(); + } + } + + private static List buildPagedFromMap(Map map) { + List out = new ArrayList<>(); + + for (Map.Entry e : map.entrySet()) { + String key = e.getKey(); + Asteroid ast = e.getValue(); + if (ast == null) continue; + + List all = AsteroidWrapper.collectOutputsFromConfig(ast); + int total = all.size(); + int pages = Math.max(1, (total + AsteroidWrapper.PAGE_SIZE - 1) / AsteroidWrapper.PAGE_SIZE); + + for (int page = 0; page < pages; page++) { + int from = page * AsteroidWrapper.PAGE_SIZE; + int to = Math.min(total, from + AsteroidWrapper.PAGE_SIZE); + List slice = (from < to) ? all.subList(from, to) : Collections.emptyList(); + + out.add(new AsteroidWrapper(key, ast, page, pages, slice)); + } + } + + // Sort: asteroid name, then page + out.sort(Comparator + .comparing(AsteroidWrapper::getDisplayName, String.CASE_INSENSITIVE_ORDER) + .thenComparingInt(AsteroidWrapper::getPageIndex)); + + return out; + } + + public static List getMachineRecipes(IJeiHelpers helpers, Class ignored) { + return getRecipes(helpers); + } + + // Optional: call this if you ever add a config-reload hook + public static void clearCache() { + cached = null; + cachedMTime = -1L; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidWrapper.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidWrapper.java new file mode 100644 index 000000000..339e0e8c4 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/asteroids/AsteroidWrapper.java @@ -0,0 +1,126 @@ +package zmaster587.advancedRocketry.integration.jei.asteroids; + +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeWrapper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; +import zmaster587.advancedRocketry.api.AdvancedRocketryItems; +import zmaster587.advancedRocketry.item.ItemAsteroidChip; +import zmaster587.advancedRocketry.util.Asteroid; + +import java.util.*; + +public class AsteroidWrapper implements IRecipeWrapper { + + // Static grid: 6x2 + public static final int COLS = 6; + public static final int ROWS = 2; + public static final int PAGE_SIZE = COLS * ROWS; // 12 + + private final String asteroidKey; + private final Asteroid asteroid; + + private final int pageIndex; // 0-based + private final int pageCount; // >= 1 + + private final ItemStack observatory; + private final ItemStack chip; + + private final List outputsVisible; // <= 12 (already sliced) + + public AsteroidWrapper(String asteroidKey, Asteroid asteroid, int pageIndex, int pageCount, List outputsVisible) { + this.asteroidKey = asteroidKey; + this.asteroid = asteroid; + this.pageIndex = Math.max(0, pageIndex); + this.pageCount = Math.max(1, pageCount); + + this.observatory = new ItemStack(AdvancedRocketryBlocks.blockObservatory); + this.chip = makeDisplayChip(asteroidKey); + + // Detach from subList backing + this.outputsVisible = (outputsVisible == null) ? Collections.emptyList() : new ArrayList<>(outputsVisible); + } + + public boolean isValid() { + return asteroid != null; + } + + public int getPageIndex() { + return pageIndex; + } + + public String getDisplayName() { + try { + String n = asteroid.getName(); + if (n != null && !n.isEmpty()) return n; + } catch (Throwable ignored) {} + return (asteroidKey != null && !asteroidKey.isEmpty()) ? asteroidKey : "Asteroid"; + } + + public String getHeaderText() { + String name = getDisplayName(); + if (pageCount > 1) { + name += " (" + (pageIndex + 1) + "/" + pageCount + ")"; + } + return name; + } + + @Override + public void getIngredients(IIngredients ing) { + List> inputs = new ArrayList<>(2); + inputs.add(Collections.singletonList(observatory)); + inputs.add(Collections.singletonList(chip)); + ing.setInputLists(mezz.jei.api.ingredients.VanillaTypes.ITEM, inputs); + + ing.setOutputLists(mezz.jei.api.ingredients.VanillaTypes.ITEM, + Collections.singletonList(outputsVisible) + ); + } + + @Override + public void drawInfo(Minecraft mc, int recipeWidth, int recipeHeight, int mouseX, int mouseY) { + if (mc == null || mc.fontRenderer == null) return; + + // Draw header per-recipe (safe; wrapper is per recipe instance) + String header = getHeaderText(); + if (header != null && !header.isEmpty()) { + GlStateManager.color(1f, 1f, 1f, 1f); + mc.fontRenderer.drawString(header, 6, 2, 0x404040); + GlStateManager.color(1f, 1f, 1f, 1f); + } + } + + private static ItemStack makeDisplayChip(String type) { + ItemStack stack = new ItemStack(AdvancedRocketryItems.itemAsteroidChip); + if (stack.getItem() instanceof ItemAsteroidChip) { + ItemAsteroidChip chip = (ItemAsteroidChip) stack.getItem(); + chip.setUUID(stack, 0L); + chip.setType(stack, type != null ? type : ""); + chip.setMaxData(stack, 1000); + } + return stack; + } + + // Option A: deterministic “sane correct view” + public static List collectOutputsFromConfig(Asteroid asteroid) { + if (asteroid == null || asteroid.itemStacks == null) return Collections.emptyList(); + + LinkedHashMap seen = new LinkedHashMap<>(); + + for (ItemStack s : asteroid.itemStacks) { + if (s == null || s.isEmpty()) continue; + if (s.getItem() == null || s.getItem().getRegistryName() == null) continue; + + ItemStack one = s.copy(); + one.setCount(1); + + String key = String.valueOf(one.getItem().getRegistryName()) + "@" + one.getMetadata(); + if (!seen.containsKey(key)) { + seen.put(key, one); + } + } + return new ArrayList<>(seen.values()); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberCategory.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberCategory.java new file mode 100644 index 000000000..298b201bc --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberCategory.java @@ -0,0 +1,50 @@ +package zmaster587.advancedRocketry.integration.jei.co2scrubber; + +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IGuiItemStackGroup; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeCategory; +import mezz.jei.api.gui.IRecipeLayout; +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; + +public class Co2ScrubberCategory implements IRecipeCategory { + + private final IDrawable bg; + private final IDrawable icon; + private final IDrawable slot; + + public Co2ScrubberCategory(IGuiHelper gui) { + this.bg = gui.createBlankDrawable(150, 40); + this.icon = gui.createDrawableIngredient(new ItemStack(AdvancedRocketryBlocks.blockCO2Scrubber)); + this.slot = gui.getSlotDrawable(); + } + + @Override public String getUid() { return ARPlugin.co2ScrubberUUID; } + @Override public String getTitle() { return new ItemStack(AdvancedRocketryBlocks.blockCO2Scrubber).getDisplayName(); } + @Override public String getModName() { return "Advanced Rocketry"; } + @Override public IDrawable getBackground(){ return bg; } + @Override public IDrawable getIcon() { return icon; } + + @Override + public void setRecipe(IRecipeLayout layout, Co2ScrubberWrapper wrapper, IIngredients ing) { + IGuiItemStackGroup items = layout.getItemStacks(); + + // One input slot (cartridge), left side + items.init(0, true, 20, 11); + items.set(0, ing.getInputs(mezz.jei.api.ingredients.VanillaTypes.ITEM).get(0)); + + // Oxygen Vent ghost on the right + items.init(1, false, 120, 11); + items.set(1, new ItemStack(AdvancedRocketryBlocks.blockOxygenVent)); + } + + @Override + public void drawExtras(Minecraft mc) { + // Draw the slot frame behind the cartridge + slot.draw(mc, 20, 11); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberRecipeHandler.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberRecipeHandler.java new file mode 100644 index 000000000..5ebc0a039 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberRecipeHandler.java @@ -0,0 +1,28 @@ +package zmaster587.advancedRocketry.integration.jei.co2scrubber; + +import mezz.jei.api.recipe.IRecipeHandler; +import mezz.jei.api.recipe.IRecipeWrapper; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; + +public class Co2ScrubberRecipeHandler implements IRecipeHandler { + + @Override + public Class getRecipeClass() { + return Co2ScrubberWrapper.class; + } + + @Override + public String getRecipeCategoryUid(Co2ScrubberWrapper recipe) { + return ARPlugin.co2ScrubberUUID; + } + + @Override + public IRecipeWrapper getRecipeWrapper(Co2ScrubberWrapper recipe) { + return recipe; + } + + @Override + public boolean isRecipeValid(Co2ScrubberWrapper recipe) { + return recipe != null && !recipe.getCartridgeStack().isEmpty(); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberRecipeMaker.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberRecipeMaker.java new file mode 100644 index 000000000..a8354d898 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberRecipeMaker.java @@ -0,0 +1,19 @@ +package zmaster587.advancedRocketry.integration.jei.co2scrubber; + +import mezz.jei.api.IJeiHelpers; +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.api.AdvancedRocketryItems; + +import java.util.ArrayList; +import java.util.List; + +public class Co2ScrubberRecipeMaker { + public static List getRecipes(IJeiHelpers helpers) { + List list = new ArrayList<>(); + + ItemStack cart = new ItemStack(AdvancedRocketryItems.itemCarbonScrubberCartridge, 1, net.minecraftforge.oredict.OreDictionary.WILDCARD_VALUE); + list.add(new Co2ScrubberWrapper(cart)); + + return list; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberWrapper.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberWrapper.java new file mode 100644 index 000000000..cbddae11f --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/co2scrubber/Co2ScrubberWrapper.java @@ -0,0 +1,34 @@ +package zmaster587.advancedRocketry.integration.jei.co2scrubber; + +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.BlankRecipeWrapper; +import net.minecraft.item.ItemStack; +import java.util.Collections; + +public class Co2ScrubberWrapper extends BlankRecipeWrapper { + private final ItemStack cartridge; + + public Co2ScrubberWrapper(ItemStack cartridge) { + this.cartridge = cartridge; + } + + @Override + public void getIngredients(IIngredients ing) { + // INPUTS: the cartridge (as a list-of-lists) + ing.setInputLists(mezz.jei.api.ingredients.VanillaTypes.ITEM, + java.util.Collections.singletonList( + java.util.Collections.singletonList(cartridge))); + + // OUTPUTS: expose BOTH blocks + the cartridge + java.util.List outs = new java.util.ArrayList<>(3); + outs.add(new net.minecraft.item.ItemStack(zmaster587.advancedRocketry.api.AdvancedRocketryBlocks.blockCO2Scrubber)); + outs.add(new net.minecraft.item.ItemStack(zmaster587.advancedRocketry.api.AdvancedRocketryBlocks.blockOxygenVent)); + outs.add(cartridge); + ing.setOutputs(mezz.jei.api.ingredients.VanillaTypes.ITEM, outs); + } + + // Used by the recipe handler's isRecipeValid + public ItemStack getCartridgeStack() { + return cartridge; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationCategory.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationCategory.java new file mode 100644 index 000000000..240586afe --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationCategory.java @@ -0,0 +1,97 @@ +package zmaster587.advancedRocketry.integration.jei.fuelingStation; + +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.*; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeCategory; +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; +import zmaster587.libVulpes.gui.CommonResources; + +public class FuelingStationCategory implements IRecipeCategory { + + private final IDrawable background; + private final IDrawable icon; + private final IDrawable tankFrame; // 14x54 bezel from generic background + private final IDrawable slotFrame; // JEI’s standard slot look + + // --- compact but accurate: 150 x 56 so 4 recipes fit on one JEI page --- + public FuelingStationCategory(IGuiHelper gui) { + this.background = gui.createBlankDrawable(150, 56); + this.icon = gui.createDrawableIngredient(new ItemStack(AdvancedRocketryBlocks.blockFuelingStation)); + + // exact bezel the in-game ModuleLiquidIndicator draws: u=176,v=58,w=14,h=54 + this.tankFrame = gui.createDrawable(CommonResources.genericBackground, 176, 58, 14, 54); + + // vanilla-looking slot border + this.slotFrame = gui.getSlotDrawable(); + } + + @Override public String getUid() { return ARPlugin.fuelingStationUUID; } + @Override public String getTitle() { return new ItemStack(AdvancedRocketryBlocks.blockFuelingStation).getDisplayName(); } + @Override public String getModName() { return "Advanced Rocketry"; } + @Override public IDrawable getBackground(){ return background; } + @Override public IDrawable getIcon() { return icon; } + + @Override + public void setRecipe(IRecipeLayout layout, FuelingStationWrapper wrapper, IIngredients ing) { + // Fluid gauge (inside the real bezel) + IGuiFluidStackGroup fluids = layout.getFluidStacks(); + fluids.init(0, true, 28, 3, 12, 52, 1000, false, null); + fluids.set(0, wrapper.getFluid()); + + IGuiItemStackGroup items = layout.getItemStacks(); + + // ITEM inputs come as two lists: [ [bucket?], [role tank] ] + java.util.List> itemInputs = + ing.getInputs(mezz.jei.api.ingredients.VanillaTypes.ITEM); + + // Slot 0: bucket INPUT (if present) + items.init(0, true, 45, 6); + if (!itemInputs.isEmpty() && !itemInputs.get(0).isEmpty() + && itemInputs.get(0).get(0).getItem() == wrapper.getFilledContainer().getItem()) { + items.set(0, itemInputs.get(0)); + } else { + items.set(0, java.util.Collections.emptyList()); + } + items.addTooltipCallback((slotIndex, input, stack, tooltip) -> { + if (slotIndex != 0 || stack == null || stack.isEmpty()) return; + + // Only decorate the bucket input slot + tooltip.add(""); + tooltip.add(net.minecraft.util.text.TextFormatting.YELLOW + + zmaster587.libVulpes.LibVulpes.proxy.getLocalizedString( + "jei.ar.fuel.role." + wrapper.getRole().langKey() + )); + }); + + // Slot 1: ROLE TANK + items.init(1, true, 120, 6); + // The role tank will be the other input list + if (itemInputs.size() >= 2) { + items.set(1, itemInputs.get(1)); + } else if (!wrapper.getRoleTankStack().isEmpty()) { + // fallback if bucket missing → the only input is the role tank + items.set(1, java.util.Collections.singletonList(wrapper.getRoleTankStack())); + } + fluids.addTooltipCallback((slotIndex, input, fluid, tooltip) -> { + if (slotIndex != 0 || fluid == null) return; + + // Blank spacer then role + usage + tooltip.add(""); + tooltip.add(net.minecraft.util.text.TextFormatting.YELLOW + + zmaster587.libVulpes.LibVulpes.proxy.getLocalizedString( + "jei.ar.fuel.role." + wrapper.getRole().langKey() + )); + }); + } + + @Override + public void drawExtras(Minecraft mc) { + tankFrame.draw(mc, 27, 2); + slotFrame.draw(mc, 45, 6); + } + +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationRecipeHandler.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationRecipeHandler.java new file mode 100644 index 000000000..efc87ce5d --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationRecipeHandler.java @@ -0,0 +1,12 @@ +package zmaster587.advancedRocketry.integration.jei.fuelingStation; + +import mezz.jei.api.recipe.IRecipeHandler; +import mezz.jei.api.recipe.IRecipeWrapper; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; + +public class FuelingStationRecipeHandler implements IRecipeHandler { + @Override public Class getRecipeClass() { return FuelingStationWrapper.class; } + @Override public String getRecipeCategoryUid(FuelingStationWrapper r) { return ARPlugin.fuelingStationUUID; } + @Override public IRecipeWrapper getRecipeWrapper(FuelingStationWrapper r) { return r; } + @Override public boolean isRecipeValid(FuelingStationWrapper r) { return r != null; } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationRecipeMaker.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationRecipeMaker.java new file mode 100644 index 000000000..ab6af1c14 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationRecipeMaker.java @@ -0,0 +1,41 @@ +package zmaster587.advancedRocketry.integration.jei.fuelingStation; + +import mezz.jei.api.IJeiHelpers; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import zmaster587.advancedRocketry.api.fuel.FuelRegistry; +import zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class FuelingStationRecipeMaker { + + public static List getRecipes(IJeiHelpers helpers) { + List out = new ArrayList<>(); + + add(out, FuelType.LIQUID_MONOPROPELLANT, FuelingStationWrapper.Role.MONO); + add(out, FuelType.LIQUID_BIPROPELLANT, FuelingStationWrapper.Role.BIPROP_FUEL); + add(out, FuelType.LIQUID_OXIDIZER, FuelingStationWrapper.Role.OXIDIZER); + add(out, FuelType.NUCLEAR_WORKING_FLUID, FuelingStationWrapper.Role.WORKING_FLUID); + + return out; + } + + private static void add(List list, + FuelType type, + FuelingStationWrapper.Role role) { + // Avoid name clash with AR’s FuelRegistry by fully-qualifying Forge’s registry here. + for (Map.Entry e : net.minecraftforge.fluids.FluidRegistry.getRegisteredFluids().entrySet()) { + Fluid f = e.getValue(); + if (f != null && FuelRegistry.instance.isFuel(type, f)) { + list.add(new FuelingStationWrapper(new FluidStack(f, 1000), role)); + } + } + } + + public static List getMachineRecipes(IJeiHelpers helpers, Class ignored) { + return getRecipes(helpers); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationWrapper.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationWrapper.java new file mode 100644 index 000000000..2c5811be2 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/fuelingStation/FuelingStationWrapper.java @@ -0,0 +1,95 @@ +package zmaster587.advancedRocketry.integration.jei.fuelingStation; + +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeWrapper; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidUtil; +import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; + +import java.util.Collections; + +public class FuelingStationWrapper implements IRecipeWrapper { + + public enum Role { + MONO("monopropellant"), BIPROP_FUEL("biprop_fuel"), + OXIDIZER("oxidizer"), WORKING_FLUID("working_fluid"); + private final String key; Role(String k){ this.key = k; } + public String langKey(){ return key; } + } + + private final FluidStack fluid; + private final Role role; + + public FuelingStationWrapper(FluidStack fluid, Role role) { + this.fluid = fluid; + this.role = role; + } + + public Role getRole() { return role; } + public FluidStack getFluid() { return fluid; } + + @Override + public void getIngredients(IIngredients ing) { + // Fluid input (internal tank) + ing.setInputs(mezz.jei.api.ingredients.VanillaTypes.FLUID, + java.util.Collections.singletonList(fluid)); + + // ITEM inputs in order: + // 0: [filled bucket?] + // 1: [role tank] + // 2: [fueling station] <-- hidden, just for discoverability via U/R on the block + java.util.List> itemInputs = new java.util.ArrayList<>(3); + + // 0) filled bucket (if present) + ItemStack filled = getFilledContainer(); + if (!filled.isEmpty()) { + itemInputs.add(java.util.Collections.singletonList(filled)); + } + + // 1) role tank (always try to include) + ItemStack roleTank = getRoleTankStack(); + if (!roleTank.isEmpty()) { + itemInputs.add(java.util.Collections.singletonList(roleTank)); + } + + // 2) fueling station (hidden ingredient so U/R on the block opens this tab) + ItemStack station = new ItemStack(zmaster587.advancedRocketry.api.AdvancedRocketryBlocks.blockFuelingStation); + itemInputs.add(java.util.Collections.singletonList(station)); + + // commit item inputs + ing.setInputLists(mezz.jei.api.ingredients.VanillaTypes.ITEM, itemInputs); + + // Outputs: keep role tank (if present) and ALSO the station (so R on the block opens this tab) + java.util.List outs = new java.util.ArrayList<>(2); + if (!roleTank.isEmpty()) outs.add(roleTank); + outs.add(station); + ing.setOutputs(mezz.jei.api.ingredients.VanillaTypes.ITEM, outs); + } + + + public ItemStack getRoleTankStack() { + switch (role) { + case MONO: + return AdvancedRocketryBlocks.blockFuelTank != null ? new ItemStack(AdvancedRocketryBlocks.blockFuelTank) : ItemStack.EMPTY; + case BIPROP_FUEL: + return AdvancedRocketryBlocks.blockBipropellantFuelTank != null ? new ItemStack(AdvancedRocketryBlocks.blockBipropellantFuelTank) : ItemStack.EMPTY; + case OXIDIZER: + return AdvancedRocketryBlocks.blockOxidizerFuelTank != null ? new ItemStack(AdvancedRocketryBlocks.blockOxidizerFuelTank) : ItemStack.EMPTY; + case WORKING_FLUID: + return AdvancedRocketryBlocks.blockNuclearFuelTank != null ? new ItemStack(AdvancedRocketryBlocks.blockNuclearFuelTank) : ItemStack.EMPTY; + default: + return ItemStack.EMPTY; + } + } + + public ItemStack getFilledContainer() { + ItemStack is = net.minecraftforge.fluids.FluidUtil.getFilledBucket(fluid); + return is == null ? ItemStack.EMPTY : is; + } + + static ItemStack fuelStationDisplayStack() { + return new ItemStack(AdvancedRocketryBlocks.blockFuelingStation); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillCategory.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillCategory.java new file mode 100644 index 000000000..7a960c301 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillCategory.java @@ -0,0 +1,102 @@ +package zmaster587.advancedRocketry.integration.jei.orbitalLaserDrill; + +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IGuiItemStackGroup; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeCategory; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; + +public class OrbitalLaserDrillCategory implements IRecipeCategory { + + private final IDrawable background; + private final IDrawable icon; + private final IDrawable slotFrame; + + private static final int SLOT = 18; + private static final int GRID = OrbitalLaserDrillWrapper.GRID; + private static final int PAD = 6; + private static final int GAP = 10; + + private static final int BG_W = PAD + SLOT + GAP + (GRID * SLOT) + PAD; + private static final int BG_H = PAD + (GRID * SLOT) + PAD; + + private static final int MACHINE_X = PAD; + private static final int MACHINE_Y = (BG_H - SLOT) / 2; + + private static final int GRID_X0 = PAD + SLOT + GAP; + private static final int GRID_Y0 = PAD; + + private String header = ""; + + public OrbitalLaserDrillCategory(IGuiHelper gui) { + this.background = gui.createBlankDrawable(BG_W, BG_H); + this.icon = gui.createDrawableIngredient(new ItemStack(AdvancedRocketryBlocks.blockSpaceLaser)); + this.slotFrame = gui.getSlotDrawable(); + } + + @Override public String getUid() { return ARPlugin.orbitalLaserDrillUUID; } + + @Override + public String getTitle() { + return new ItemStack(AdvancedRocketryBlocks.blockSpaceLaser).getDisplayName(); + } + + @Override public String getModName() { return "Advanced Rocketry"; } + @Override public IDrawable getBackground() { return background; } + @Override public IDrawable getIcon() { return icon; } + + @Override + public void setRecipe(IRecipeLayout layout, OrbitalLaserDrillWrapper wrapper, IIngredients ing) { + this.header = (wrapper != null) ? wrapper.getHeaderText() : ""; + + IGuiItemStackGroup items = layout.getItemStacks(); + + items.init(0, true, MACHINE_X, MACHINE_Y); + + int slot = 1; + for (int row = 0; row < GRID; row++) { + for (int col = 0; col < GRID; col++) { + items.init(slot, false, GRID_X0 + col * SLOT, GRID_Y0 + row * SLOT); + slot++; + } + } + + java.util.List> inLists = + ing.getInputs(mezz.jei.api.ingredients.VanillaTypes.ITEM); + if (!inLists.isEmpty()) { + items.set(0, inLists.get(0)); + } + + java.util.List> outLists = + ing.getOutputs(mezz.jei.api.ingredients.VanillaTypes.ITEM); + + if (!outLists.isEmpty() && !outLists.get(0).isEmpty()) { + java.util.List outs = outLists.get(0); + for (int i = 0; i < (GRID * GRID); i++) { + int jeiSlot = 1 + i; + if (i < outs.size()) { + items.set(jeiSlot, outs.get(i)); + } + } + } + } + + @Override + public void drawExtras(Minecraft mc) { + // Slot frame for machine + slotFrame.draw(mc, MACHINE_X, MACHINE_Y); + + // Slot frames for grid + for (int row = 0; row < GRID; row++) { + for (int col = 0; col < GRID; col++) { + slotFrame.draw(mc, GRID_X0 + col * SLOT, GRID_Y0 + row * SLOT); + } + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillRecipeHandler.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillRecipeHandler.java new file mode 100644 index 000000000..a5014af67 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillRecipeHandler.java @@ -0,0 +1,12 @@ +package zmaster587.advancedRocketry.integration.jei.orbitalLaserDrill; + +import mezz.jei.api.recipe.IRecipeHandler; +import mezz.jei.api.recipe.IRecipeWrapper; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; + +public class OrbitalLaserDrillRecipeHandler implements IRecipeHandler { + @Override public Class getRecipeClass() { return OrbitalLaserDrillWrapper.class; } + @Override public String getRecipeCategoryUid(OrbitalLaserDrillWrapper r) { return ARPlugin.orbitalLaserDrillUUID; } + @Override public IRecipeWrapper getRecipeWrapper(OrbitalLaserDrillWrapper r) { return r; } + @Override public boolean isRecipeValid(OrbitalLaserDrillWrapper r) { return r != null; } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillRecipeMaker.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillRecipeMaker.java new file mode 100644 index 000000000..74cf6813f --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillRecipeMaker.java @@ -0,0 +1,52 @@ +package zmaster587.advancedRocketry.integration.jei.orbitalLaserDrill; + +import mezz.jei.api.IJeiHelpers; +import net.minecraft.client.Minecraft; +import net.minecraft.world.World; +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.api.ARConfiguration; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.dimension.DimensionProperties; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class OrbitalLaserDrillRecipeMaker { + + public static List getRecipes(IJeiHelpers helpers) { + + // Only show this page if VoidDrill is active + if (ARConfiguration.getCurrentConfig().laserDrillPlanet) { + return Collections.emptyList(); + } + + World w = Minecraft.getMinecraft() != null ? Minecraft.getMinecraft().world : null; + DimensionProperties props = null; + + if (w != null && w.provider != null) { + props = DimensionManager.getInstance().getDimensionProperties(w.provider.getDimension()); + } + + List all = OrbitalLaserDrillWrapper.buildVoidDrillActivationList(props); + int total = all.size(); + + int pages = Math.max(1, (total + OrbitalLaserDrillWrapper.PAGE_SIZE - 1) / OrbitalLaserDrillWrapper.PAGE_SIZE); + + List out = new ArrayList<>(pages); + + for (int page = 0; page < pages; page++) { + int from = page * OrbitalLaserDrillWrapper.PAGE_SIZE; + int to = Math.min(total, from + OrbitalLaserDrillWrapper.PAGE_SIZE); + + List slice = (from < to) ? all.subList(from, to) : Collections.emptyList(); + out.add(new OrbitalLaserDrillWrapper(page, pages, slice)); + } + + return out; + } + + public static List getMachineRecipes(IJeiHelpers helpers, Class ignored) { + return getRecipes(helpers); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillWrapper.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillWrapper.java new file mode 100644 index 000000000..2fb3b1428 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/orbitalLaserDrill/OrbitalLaserDrillWrapper.java @@ -0,0 +1,139 @@ +package zmaster587.advancedRocketry.integration.jei.orbitalLaserDrill; + +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeWrapper; +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; +import zmaster587.advancedRocketry.api.ARConfiguration; +import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; +import zmaster587.advancedRocketry.dimension.DimensionProperties; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +public class OrbitalLaserDrillWrapper implements IRecipeWrapper { + + public static final int GRID = 6; + public static final int PAGE_SIZE = GRID * GRID; // 36 + + private final ItemStack machine; + private final List outputsPage; + + private final int pageIndex; // 0-based + private final int pageCount; // >= 1 + + public OrbitalLaserDrillWrapper(int pageIndex, int pageCount, List outputsPage) { + this.machine = new ItemStack(AdvancedRocketryBlocks.blockSpaceLaser); + this.pageIndex = pageIndex; + this.pageCount = Math.max(1, pageCount); + this.outputsPage = outputsPage == null ? Collections.emptyList() : new ArrayList<>(outputsPage); + } + + public String getHeaderText() { + String name = machine.getDisplayName(); + if (pageCount > 1) { + name += " (" + (pageIndex + 1) + "/" + pageCount + ")"; + } + return name; + } + + @Override + public void getIngredients(IIngredients ing) { + // Input: machine block (discoverability) + List> inputs = new ArrayList<>(1); + inputs.add(Collections.singletonList(machine)); + ing.setInputLists(mezz.jei.api.ingredients.VanillaTypes.ITEM, inputs); + + // Output: one list shown in the grid + ing.setOutputLists(mezz.jei.api.ingredients.VanillaTypes.ITEM, + Collections.singletonList(outputsPage) + ); + } + + // ---- shared builder used by the RecipeMaker ---- + + public static List buildVoidDrillActivationList(DimensionProperties dimPropsOrNull) { + List ores = new ArrayList<>(); + HashSet seenKeys = new HashSet<>(); + + // 1) Global list from config + List configOres = ARConfiguration.getCurrentConfig().standardLaserDrillOres; + if (configOres != null) { + for (String oreDictName : configOres) { + ItemStack stack = parseConfigEntryToStack(oreDictName); + if (!stack.isEmpty()) { + String key = key(stack); + if (seenKeys.add(key)) ores.add(stack); + } + } + } + + // 2) Dimension-specific additions (matches VoidDrill.activate behavior) + if (dimPropsOrNull != null && dimPropsOrNull.laserDrillOres != null) { + for (ItemStack s : dimPropsOrNull.laserDrillOres) { + if (s == null || s.isEmpty()) continue; + ItemStack copy = s.copy(); + String key = key(copy); + if (seenKeys.add(key)) ores.add(copy); + } + } + + return ores; + } + + private static ItemStack parseConfigEntryToStack(String oreDictName) { + if (oreDictName == null || oreDictName.isEmpty()) return ItemStack.EMPTY; + + String[] args = oreDictName.split(":"); + + // OreDict first: "oreIron:2" + List globalOres = OreDictionary.getOres(args[0]); + if (globalOres != null && !globalOres.isEmpty()) { + int amt = 1; + if (args.length > 1) { + try { amt = Integer.parseInt(args[1]); } catch (NumberFormatException ignored) {} + } + ItemStack base = globalOres.get(0); + return new ItemStack(base.getItem(), amt, base.getItemDamage()); + } + + // Fallback: "modid:blockname[:meta[:size]]" + String name; + try { + name = args[0] + ":" + args[1]; + } catch (IndexOutOfBoundsException e) { + return ItemStack.EMPTY; + } + + int meta = 0; + int size = 1; + + if (args.length > 2) { + try { meta = Integer.parseInt(args[2]); } catch (NumberFormatException ignored) {} + } + if (args.length > 3) { + try { size = Integer.parseInt(args[3]); } catch (NumberFormatException ignored) {} + } + + Block block = Block.getBlockFromName(name); + if (block != null && block != Blocks.AIR) { + return new ItemStack(block, size, meta); + } + + Item item = Item.getByNameOrId(name); + if (item != null) { + return new ItemStack(item, size, meta); + } + + return ItemStack.EMPTY; + } + + private static String key(ItemStack s) { + return s.getItem().getRegistryName() + "@" + s.getItemDamage() + "x" + s.getCount(); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderCategory.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderCategory.java new file mode 100644 index 000000000..0a303e02f --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderCategory.java @@ -0,0 +1,152 @@ +package zmaster587.advancedRocketry.integration.jei.satelliteBuilder; + +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeCategory; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.Gui; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; +import zmaster587.advancedRocketry.inventory.TextureResources; +import zmaster587.libVulpes.LibVulpes; + +import java.util.List; + +public class SatelliteBuilderCategory implements IRecipeCategory { + + private final IDrawable background; + private final IDrawable slotFunctionComponent; + private final IDrawable slotPowerComponent; + private final IDrawable slotIO; + private final IDrawable slotSatellite; + private final IDrawable slotIdChip; + private final IDrawable progressBar; + private final IDrawable vanillaSlot; + private final String uid; + + public SatelliteBuilderCategory(IGuiHelper guiHelper) { + // Use a blank or minimal background (176x90 is enough for the elements) + this.background = guiHelper.createBlankDrawable(176, 90); + + // Slot frames/icons from TextureResources + this.slotFunctionComponent = guiHelper.createDrawable( + new ResourceLocation("libvulpes:textures/gui/maingui.png"), + 212, 18, 18, 18); // functionComponent + + this.slotPowerComponent = guiHelper.createDrawable( + new ResourceLocation("libvulpes:textures/gui/maingui.png"), + 230, 18, 18, 18); // powercomponent + + this.slotIO = guiHelper.createDrawable( + new ResourceLocation("libvulpes:textures/gui/maingui.png"), + 212, 0, 18, 18); // ioSlot + + this.slotSatellite = guiHelper.createDrawable( + new ResourceLocation("advancedrocketry:textures/gui/progressBars/progressBars.png"), + 220, 238, 18, 18); + + this.slotIdChip = guiHelper.createDrawable( + new ResourceLocation("libvulpes:textures/gui/maingui.png"), + 230, 0, 18, 18); // idChip + + this.vanillaSlot = guiHelper.getSlotDrawable(); + + this.progressBar = guiHelper.createDrawable( + new ResourceLocation("advancedrocketry:textures/gui/progressBars/progressBars.png"), + 217, 0, 17, 17); // progressBar + + this.uid = ARPlugin.satelliteBuilderUUID; + } + + @Override + public IDrawable getBackground() { + return background; + } + + @Override + public String getTitle() { + return LibVulpes.proxy.getLocalizedString("tile.satelliteBuilder.name"); + } + + @Override + public String getModName() { + return "Advanced Rocketry"; + } + + @Override + public String getUid() { + return uid; + } + + public Class getRecipeClass() { + return SatelliteBuilderWrapper.class; + } + + + @Override + public void setRecipe(IRecipeLayout recipeLayout, SatelliteBuilderWrapper wrapper, IIngredients ingredients) { + // Place JEI slots at the same coordinates as the modules + // Slot indices: see TileSatelliteBuilder for mapping + // 0: function, 1-6: IO, 7: Output, 8: Chip, 9: Chip, 10: Chip copy 11: chassis + // Function slot 0 + recipeLayout.getItemStacks().init(0, true, 152, 10); + + // Power slots 1-2-3 + recipeLayout.getItemStacks().init(1, true, 116, 30); + recipeLayout.getItemStacks().init(2, true, 134, 30); + recipeLayout.getItemStacks().init(3, true, 152, 30); + recipeLayout.getItemStacks().init(4, true, 116, 50); + recipeLayout.getItemStacks().init(5, true, 134, 50); + recipeLayout.getItemStacks().init(6, true, 152, 50); + + // Output slot 7 + recipeLayout.getItemStacks().init(7, false, 58, 36); + + // ID chip slot 8 + recipeLayout.getItemStacks().init(8, true, 58, 16); + + // Chip copy slot 9 + recipeLayout.getItemStacks().init(9, true, 82, 16); + + // holdingslot slot 10 not used by players + //recipeLayout.getItemStacks().init(10, false, 58, 36); + + // Chassis slot 11 + recipeLayout.getItemStacks().init(11, true, 38, 16); + + recipeLayout.getItemStacks().set(ingredients); + + // Add tooltip cosmetics + wrapper.registerTooltipCallbacks(recipeLayout.getItemStacks()); + } + + @Override + public void drawExtras(Minecraft minecraft) { + FontRenderer fr = minecraft.fontRenderer; + // Draw slot frames and icons at the correct positions + slotFunctionComponent.draw(minecraft, 152, 10); // slot 0 + slotPowerComponent.draw(minecraft, 116, 30); // slot 1 + slotPowerComponent.draw(minecraft, 134, 30); // slot 2 + slotPowerComponent.draw(minecraft, 152, 30); // slot 3 + slotIO.draw(minecraft, 116, 50); // slot 4 + slotIO.draw(minecraft, 134, 50); // slot 5 + slotIO.draw(minecraft, 152, 50); // slot 6 + vanillaSlot.draw(minecraft, 58, 36); // Output slot (slot 7) + slotIdChip.draw(minecraft, 58, 16); // slot 8 + slotIdChip.draw(minecraft, 82, 16); // slot 9 + slotSatellite.draw(minecraft, 38, 16); // Chassis slot (slot 11) + + // Progress bar + progressBar.draw(minecraft, 75, 36); + + } + @Override + public java.util.List getTooltipStrings(int mouseX, int mouseY) { + return java.util.Collections.emptyList(); + } + +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderRecipeHandler.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderRecipeHandler.java new file mode 100644 index 000000000..adaa2e443 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderRecipeHandler.java @@ -0,0 +1,28 @@ +package zmaster587.advancedRocketry.integration.jei.satelliteBuilder; + +import mezz.jei.api.recipe.IRecipeHandler; +import mezz.jei.api.recipe.IRecipeWrapper; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; + +public class SatelliteBuilderRecipeHandler implements IRecipeHandler { + + @Override + public Class getRecipeClass() { + return SatelliteBuilderWrapper.class; + } + + @Override + public String getRecipeCategoryUid(SatelliteBuilderWrapper recipe) { + return ARPlugin.satelliteBuilderUUID; + } + + @Override + public IRecipeWrapper getRecipeWrapper(SatelliteBuilderWrapper recipe) { + return recipe; + } + + @Override + public boolean isRecipeValid(SatelliteBuilderWrapper recipe) { + return recipe != null; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderRecipeMaker.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderRecipeMaker.java new file mode 100644 index 000000000..2827a502f --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderRecipeMaker.java @@ -0,0 +1,168 @@ +package zmaster587.advancedRocketry.integration.jei.satelliteBuilder; + +import mezz.jei.api.gui.ITooltipCallback; +import mezz.jei.api.IJeiHelpers; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import zmaster587.advancedRocketry.api.AdvancedRocketryItems; +import zmaster587.advancedRocketry.integration.jei.satelliteBuilder.SatelliteBuilderWrapper; +import zmaster587.libVulpes.api.LibVulpesItems; +import zmaster587.libVulpes.interfaces.IRecipe; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class SatelliteBuilderRecipeMaker { + + public static List getMachineRecipes(IJeiHelpers helpers, Class clazz) { + List recipes = new ArrayList<>(); + + // --- Satellite Assembly Example --- + // Slot mapping: + // 0: Function component (core module) + // 1-6: Module components (power, IO, etc) + // 7: Output slot + // 8: ID chip slot (input) + // 9: Chip copy slot (input) + // 10: Holding slot (ghostslot, not used by player) + // 11: Chassis slot + + + // slot 0 + List coreModules = new ArrayList<>(); + for (int i = 0; i < 7; i++) { + coreModules.add(new ItemStack(AdvancedRocketryItems.itemSatellitePrimaryFunction, 1, i)); + } // slot 0 + + // slots 1-6: power gen, battery, data units + List moduleVariants = Arrays.asList( + new ItemStack(AdvancedRocketryItems.itemSatellitePowerSource, 1, 0), + new ItemStack(AdvancedRocketryItems.itemSatellitePowerSource, 1, 1), + new ItemStack(LibVulpesItems.itemBattery, 1, 0), + new ItemStack(LibVulpesItems.itemBattery, 1, 1), + new ItemStack(AdvancedRocketryItems.itemDataUnit, 1, 0) + ); + + + + // Slot 8: controllers mapped 1:1 to primariry core modules (metas 0-6) + List satelliteControllers = Arrays.asList( + new ItemStack(AdvancedRocketryItems.itemSatelliteIdChip), // 0: Optical + new ItemStack(AdvancedRocketryItems.itemSatelliteIdChip), // 1: Composition + new ItemStack(AdvancedRocketryItems.itemSatelliteIdChip), // 2: Mass Scanner + new ItemStack(AdvancedRocketryItems.itemSatelliteIdChip), // 3: Microwave Energy + new ItemStack(AdvancedRocketryItems.itemOreScanner), // 4: Ore Mapping + new ItemStack(AdvancedRocketryItems.itemBiomeChanger), // 5: Biome Changer (remote) + new ItemStack(AdvancedRocketryItems.itemWeatherController)// 6: Weather Controller (remote) + ); + + + List output = Collections.singletonList(new ItemStack(AdvancedRocketryItems.itemSatellite)); // slot 7 (output) + List chassis = Collections.singletonList(new ItemStack(AdvancedRocketryItems.itemSatellite)); // slot 11 (empty chassis) + + List> inputs = new ArrayList<>(); + inputs.add(coreModules); // slot 0: Function component + inputs.add(moduleVariants); // slot 1: Module component + inputs.add(moduleVariants); // slot 2: Module component + inputs.add(moduleVariants); // slot 3: Module component + inputs.add(moduleVariants); // slot 4: Module component + inputs.add(moduleVariants); // slot 5: Module component + inputs.add(moduleVariants); // slot 6: Module component + //inputs.add(Collections.emptyList()); // slot 7: Output slot (not used as input) + inputs.add(satelliteControllers); // slot 8: ID chip slot + inputs.add(Collections.emptyList()); // slot 9: Chip copy slot (not used in this recipe) + //inputs.add(Collections.emptyList()); // slot 10: Holding slot (ghostslot, not used) + inputs.add(chassis); // slot 11: Chassis slot + + // Anonymous IRecipe implementation + IRecipe assemblyRecipe = new IRecipe() { + @Override + public List getOutput() { return output; } // slot 7 + @Override + public List getFluidOutputs() { return Collections.emptyList(); } + @Override + public List> getIngredients() { return inputs; } + @Override + public List getFluidIngredients() { return Collections.emptyList(); } + @Override + public int getTime() { return 200; } + @Override + public int getPower() { return 0; } + @Override + public String getOreDictString(int var1) { return ""; } + }; + + recipes.add(new SatelliteBuilderWrapper(assemblyRecipe, false)); + + // --- Chip Copy Example --- + // Slot mapping for chip copy: + // 8: Source chip (input) + // 9: Blank chip (input) + // 7: Output slot (copied chip) + + List sourceChips = Arrays.asList( + new ItemStack(AdvancedRocketryItems.itemSatelliteIdChip), + new ItemStack(AdvancedRocketryItems.itemPlanetIdChip), + new ItemStack(AdvancedRocketryItems.itemSpaceStationChip), + new ItemStack(AdvancedRocketryItems.itemOreScanner), + new ItemStack(AdvancedRocketryItems.itemBiomeChanger), + new ItemStack(AdvancedRocketryItems.itemWeatherController), + new ItemStack(AdvancedRocketryItems.itemSpaceElevatorChip) + ); + + // Mirror the source chips for blank chips + List blankChips = sourceChips; + + // The output cycling in mapped order: + List copiedOutputVariants = Arrays.asList( + new ItemStack(AdvancedRocketryItems.itemSatelliteIdChip), + new ItemStack(AdvancedRocketryItems.itemPlanetIdChip), + new ItemStack(AdvancedRocketryItems.itemSpaceStationChip), + new ItemStack(AdvancedRocketryItems.itemOreScanner), + new ItemStack(AdvancedRocketryItems.itemBiomeChanger), + new ItemStack(AdvancedRocketryItems.itemWeatherController), + new ItemStack(AdvancedRocketryItems.itemSpaceElevatorChip) + ); + + List> chipCopyInputs = new ArrayList<>(); + chipCopyInputs.add(Collections.emptyList()); // slot 0: Function component (not used) + chipCopyInputs.add(Collections.emptyList()); // slot 1: Power component (not used) + chipCopyInputs.add(Collections.emptyList()); // slot 2: Power component (not used) + chipCopyInputs.add(Collections.emptyList()); // slot 3: Power component (not used) + chipCopyInputs.add(Collections.emptyList()); // slot 4: IO component (not used) + chipCopyInputs.add(Collections.emptyList()); // slot 5: IO component (not used) + chipCopyInputs.add(Collections.emptyList()); // slot 6: IO component (not used) + //chipCopyInputs.add(Collections.emptyList()); // slot 7: Output slot + chipCopyInputs.add(sourceChips); // slot 8: Source chip + chipCopyInputs.add(blankChips); // slot 9: Blank chip + //chipCopyInputs.add(Collections.emptyList()); // slot 10: Holding slot (not used) + chipCopyInputs.add(Collections.emptyList()); // slot 11: Chassis slot (not used) + + IRecipe chipCopyRecipe = new IRecipe() { + @Override public List getOutput() { + // Could return first as a fallback; the wrapper will override with setOutputLists + return java.util.Collections.singletonList(copiedOutputVariants.get(0)); + } + @Override + public List getFluidOutputs() { return Collections.emptyList(); } + @Override + public List> getIngredients() { return chipCopyInputs; } + @Override + public List getFluidIngredients() { return Collections.emptyList(); } + @Override + public int getTime() { return 200; } + @Override + public int getPower() { return 0; } + @Override + public String getOreDictString(int var1) { return ""; } + }; + + recipes.add(new SatelliteBuilderWrapper(chipCopyRecipe, true, copiedOutputVariants)); + + return recipes; + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderWrapper.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderWrapper.java new file mode 100644 index 000000000..4c6943055 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/satelliteBuilder/SatelliteBuilderWrapper.java @@ -0,0 +1,147 @@ +package zmaster587.advancedRocketry.integration.jei.satelliteBuilder; + +import mezz.jei.api.gui.IGuiItemStackGroup; +import mezz.jei.api.gui.ITooltipCallback; +import mezz.jei.api.ingredients.IIngredients; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.resources.I18n; +import net.minecraft.item.ItemStack; +import net.minecraft.util.text.TextFormatting; +import zmaster587.advancedRocketry.api.AdvancedRocketryItems; +import zmaster587.advancedRocketry.integration.jei.MachineRecipe; +import zmaster587.libVulpes.LibVulpes; +import zmaster587.libVulpes.interfaces.IRecipe; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class SatelliteBuilderWrapper extends MachineRecipe { + public final boolean isCopyRecipe; + private final IRecipe baseRecipe; // keep a handle + private final List outputVariants; // for JEI cycling (copy recipe) + private static Set COPY_STRIP_STRINGS; + + public SatelliteBuilderWrapper(IRecipe rec, boolean isCopyRecipe) { + this(rec, isCopyRecipe, null); + } + + public SatelliteBuilderWrapper(IRecipe rec, boolean isCopyRecipe, List outputVariants) { + super(rec); + this.isCopyRecipe = isCopyRecipe; + this.baseRecipe = rec; + this.outputVariants = outputVariants; + } + + @Override + public void getIngredients(mezz.jei.api.ingredients.IIngredients ingredients) { + super.getIngredients(ingredients); // inputs already mapped + + if (isCopyRecipe) { + // ONE output slot cycling MANY variants + List variants = (outputVariants != null && !outputVariants.isEmpty()) + ? outputVariants + : baseRecipe.getOutput(); // fallback if you didn’t pass variants + + if (variants != null && !variants.isEmpty()) { + ingredients.setOutputLists(ItemStack.class, + java.util.Collections.singletonList(variants)); + } + } else { + // Assembly: single concrete output (first item) + List outs = baseRecipe.getOutput(); + if (outs != null && !outs.isEmpty()) { + ingredients.setOutput(ItemStack.class, outs.get(0)); + } + } + } + + + + public java.util.List getOutputVariants() { return outputVariants; } + + public void registerTooltipCallbacks(IGuiItemStackGroup stacks) { + final boolean isCopy = this.isCopyRecipe; + + stacks.addTooltipCallback(new ITooltipCallback() { + @Override + public void onTooltip(int slotIndex, boolean input, ItemStack stack, List tooltip) { + if (stack.isEmpty()) return; + + if (!isCopy) { + // === ASSEMBLY RECIPE === (only touch output slot) + if (slotIndex != 7) return; + + if (stack.getItem() == AdvancedRocketryItems.itemSatellite) { + // Remove "empty chassis" (whatever the localization) + String libEmpty = LibVulpes.proxy.getLocalizedString("msg.itemsatellite.empty"); + tooltip.removeIf(line -> { + String stripped = net.minecraft.util.text.TextFormatting.getTextWithoutFormattingCodes(line); + if (stripped == null) return false; + String s = stripped.trim(); + return s.equalsIgnoreCase(libEmpty) || s.equalsIgnoreCase("unprogrammed"); + }); + + // Add clean preview label if not already present + String label = I18n.format("jei.sb.satellitepreview"); + addIfMissing(tooltip, label); + } + return; + } + + // === CHIP-COPY RECIPE === + if (slotIndex == 7 || slotIndex == 8) { + ensureCopyStripStringsBuilt(); + + // Strip any "unprogrammed"/blank-style lines (locale + color safe) + tooltip.removeIf(line -> { + String stripped = net.minecraft.util.text.TextFormatting.getTextWithoutFormattingCodes(line); + return stripped != null && COPY_STRIP_STRINGS.contains(stripped.trim().toLowerCase()); + }); + + // Add concise labels + final String key = (slotIndex == 7) ? "jei.sb.copy.output" : "jei.sb.copy.source"; + String label = I18n.format(key); + addIfMissing(tooltip, label); + } + } + }); + } + + private static void addIfMissing(List tooltip, String label) { + for (String l : tooltip) { + String stripped = net.minecraft.util.text.TextFormatting.getTextWithoutFormattingCodes(l); + if (stripped != null && stripped.equalsIgnoreCase(label)) return; + } + tooltip.add(label); + } + + private static void ensureCopyStripStringsBuilt() { + if (COPY_STRIP_STRINGS != null) return; + COPY_STRIP_STRINGS = new HashSet<>(); + String[] keys = { + "msg.itemchip.unprogrammed", + "msg.satelliteidchip.unprogrammed", + "msg.planetidchip.unprogrammed", + "msg.stationchip.unprogrammed", + "msg.orescanner.unprogrammed", + "msg.itemsatellite.empty" + }; + for (String k : keys) { + String v1 = I18n.format(k); + if (v1 != null) COPY_STRIP_STRINGS.add(v1.trim().toLowerCase()); + String v2 = LibVulpes.proxy.getLocalizedString(k); + if (v2 != null) COPY_STRIP_STRINGS.add(v2.trim().toLowerCase()); + } + COPY_STRIP_STRINGS.add("unprogrammed"); + } + + @Override + public void drawInfo(Minecraft minecraft, int recipeWidth, int recipeHeight, int mouseX, int mouseY) { + FontRenderer fr = minecraft.fontRenderer; + String text = I18n.format(isCopyRecipe ? "jei.sb.copychiphint" : "jei.sb.assemblyhint"); + int tw = fr.getStringWidth(text); + fr.drawString(text, (recipeWidth - tw) / 2, recipeHeight - fr.FONT_HEIGHT - 4, 0x000000); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerCategory.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerCategory.java new file mode 100644 index 000000000..0b3bfcff8 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerCategory.java @@ -0,0 +1,156 @@ +package zmaster587.advancedRocketry.integration.jei.stationAssembler; + +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.IGuiItemStackGroup; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IDrawableAnimated; +import mezz.jei.api.gui.IDrawableAnimated.StartDirection; +import mezz.jei.api.gui.IDrawableStatic; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeCategory; +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; +import zmaster587.libVulpes.LibVulpes; + +/** + * Compact layout, mirroring the simple two-input / two-output flow: + * Inputs: [Satellite Loader (meta 1)] [Station Chip] + * Outputs: [Packed Station] [Station Chip (when new station)] + */ +public class StationAssemblerCategory implements IRecipeCategory { + + private final IDrawable background; + private final IDrawable icon; + private final IDrawable slotFrame; + private static final int BG_W = 180; + private static final int BG_H = 90; + + private static final ResourceLocation ROCKET_BUILDER_PNG = + new ResourceLocation("advancedrocketry", "textures/gui/rocketBuilder.png"); + + private static final int PB_BACK_U = 76, PB_BACK_V = 93, PB_BACK_W = 8, PB_BACK_H = 52; + private static final int PB_FILL_U = 176, PB_FILL_V = 15, PB_FILL_W = 2, PB_FILL_H = 38; + private static final int PB_INSET_X = 3, PB_INSET_Y = 2; + private static final int ANIM_MS = 100; + private final IDrawable backBar; // background frame (8x52) + private final IDrawableStatic fillStatic; // fill slice (2x38) + private final IDrawableAnimated fillAnim; // animated fill (bottom→top) + private int _x0, _x1, _y0, _y1; + private final int barX; + private final int barY; + + public StationAssemblerCategory(IGuiHelper gui) { + this.background = gui.createBlankDrawable(BG_W, BG_H); + this.icon = gui.createDrawableIngredient( + new net.minecraft.item.ItemStack(zmaster587.advancedRocketry.api.AdvancedRocketryBlocks.blockStationBuilder)); + this.slotFrame = gui.getSlotDrawable(); + + // build drawables from the exact atlas slices + this.backBar = gui.createDrawable(ROCKET_BUILDER_PNG, PB_BACK_U, PB_BACK_V, PB_BACK_W, PB_BACK_H); + this.fillStatic = gui.createDrawable(ROCKET_BUILDER_PNG, PB_FILL_U, PB_FILL_V, PB_FILL_W, PB_FILL_H); + this.fillAnim = gui.createAnimatedDrawable(fillStatic, ANIM_MS, StartDirection.BOTTOM, /*inverted*/ false); + + // position: right edge, centered Y + this.barX = BG_W - PB_BACK_W; + this.barY = (BG_H - PB_BACK_H) / 2; + } + + @Override public String getUid() { return ARPlugin.stationAssemblerUUID; } + @Override + public String getTitle() { + return new net.minecraft.item.ItemStack( + zmaster587.advancedRocketry.api.AdvancedRocketryBlocks.blockStationBuilder + ).getDisplayName(); + } + @Override public String getModName() { return "Advanced Rocketry"; } + @Override public IDrawable getBackground(){ return background; } + @Override public IDrawable getIcon() { return icon; } + + // keep your BG_W, BG_H, progress bar fields as-is... + + @Override + public void setRecipe(IRecipeLayout layout, StationAssemblerWrapper wrapper, IIngredients ing) { + IGuiItemStackGroup items = layout.getItemStacks(); + + // compact columns; roomy rows + final int SLOT = 18; + final int COL_GAP = 2; // close together horizontally + final int ROW_GAP = 24; // more empty space vertically + + final int widthNeeded = SLOT * 2 + COL_GAP; // 38 + final int heightNeeded = SLOT * 2 + ROW_GAP; // 60 + final int left = (BG_W - widthNeeded) / 2; + final int top = (BG_H - heightNeeded) / 2; + + final int x0 = left; + final int x1 = left + SLOT + COL_GAP; + final int y0 = top; + final int y1 = top + SLOT + ROW_GAP; + + // row 1: [ bay | empty chip ] + items.init(0, true, x0, y0); // bay (Satellite Loader meta 1) + items.init(1, true, x1, y0); // empty chip + + // row 2: [ packed item | programmed chip ] + items.init(2, false, x0, y1); // packed station + items.init(3, false, x1, y1); // programmed chip + + // bind ingredients + java.util.List> inLists = + ing.getInputs(mezz.jei.api.ingredients.VanillaTypes.ITEM); + java.util.List> outLists = + ing.getOutputs(mezz.jei.api.ingredients.VanillaTypes.ITEM); + + if (inLists.size() >= 1) items.set(0, inLists.get(0)); + if (inLists.size() >= 2) items.set(1, inLists.get(1)); + if (outLists.size() >= 1) items.set(2, outLists.get(0)); + if (outLists.size() >= 2) items.set(3, outLists.get(1)); + + // programmed chip: strip original tooltip, show only our hint + items.addTooltipCallback((slot, input, stack, tooltip) -> { + if (slot != 3 || stack == null || stack.isEmpty() + || !(stack.getItem() instanceof zmaster587.advancedRocketry.item.ItemStationChip)) return; + + // Only when chip is unprogrammed + if (zmaster587.advancedRocketry.item.ItemStationChip.getUUID(stack) == 0) { + // Vanilla "unprogrammed" text (with and without gray formatting) + final String vanilla = zmaster587.libVulpes.LibVulpes.proxy.getLocalizedString("msg.unprogrammed"); + final String vanillaGray = net.minecraft.util.text.TextFormatting.GRAY + vanilla; + + // Strip just that line (handle formatting/no-format) + tooltip.removeIf(line -> + line.equals(vanillaGray) || + line.equals(vanilla) || + net.minecraft.util.text.TextFormatting.getTextWithoutFormattingCodes(line).equals(vanilla) + ); + + // Insert our JEI-specific hint + tooltip.add(net.minecraft.util.text.TextFormatting.GRAY + + zmaster587.libVulpes.LibVulpes.proxy.getLocalizedString( + "jei.ar.stationAssembler.newStationChipHint" + ) + ); + } + }); + + + // keep for drawing slot frames + this._x0 = x0; this._x1 = x1; this._y0 = y0; this._y1 = y1; + } + + @Override + public void drawExtras(Minecraft mc) { + // progress bar (exact sprite, right-aligned, centered Y) + backBar.draw(mc, barX, barY); + fillAnim.draw(mc, barX + PB_INSET_X, barY + PB_INSET_Y); + + // draw slot frames at the centered positions + slotFrame.draw(mc, _x0, _y0); + slotFrame.draw(mc, _x1, _y0); + slotFrame.draw(mc, _x0, _y1); + slotFrame.draw(mc, _x1, _y1); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerRecipeHandler.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerRecipeHandler.java new file mode 100644 index 000000000..8ff1af524 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerRecipeHandler.java @@ -0,0 +1,12 @@ +package zmaster587.advancedRocketry.integration.jei.stationAssembler; + +import mezz.jei.api.recipe.IRecipeHandler; +import mezz.jei.api.recipe.IRecipeWrapper; +import zmaster587.advancedRocketry.integration.jei.ARPlugin; + +public class StationAssemblerRecipeHandler implements IRecipeHandler { + @Override public Class getRecipeClass() { return StationAssemblerWrapper.class; } + @Override public String getRecipeCategoryUid(StationAssemblerWrapper r) { return ARPlugin.stationAssemblerUUID; } + @Override public IRecipeWrapper getRecipeWrapper(StationAssemblerWrapper r) { return r; } + @Override public boolean isRecipeValid(StationAssemblerWrapper r) { return r != null; } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerRecipeMaker.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerRecipeMaker.java new file mode 100644 index 000000000..2209d379c --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerRecipeMaker.java @@ -0,0 +1,17 @@ +package zmaster587.advancedRocketry.integration.jei.stationAssembler; + +import mezz.jei.api.IJeiHelpers; +import java.util.Collections; +import java.util.List; + +/** Single illustrative entry: the Station Assembler has no craft-list; it’s a process gate. */ +public class StationAssemblerRecipeMaker { + public static List getRecipes(IJeiHelpers helpers) { + return java.util.Collections.singletonList(new StationAssemblerWrapper()); + } + + // Keep this to match existing pattern in your makers + public static List getMachineRecipes(IJeiHelpers helpers, Class ignored) { + return getRecipes(helpers); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerWrapper.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerWrapper.java new file mode 100644 index 000000000..496a26307 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/stationAssembler/StationAssemblerWrapper.java @@ -0,0 +1,68 @@ +package zmaster587.advancedRocketry.integration.jei.stationAssembler; + +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.IRecipeWrapper; +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; +import zmaster587.advancedRocketry.api.AdvancedRocketryItems; + +/** + * JEI wrapper for the Station Assembler flow shown in TileStationAssembler. + * Inputs (by code): + * - slot 0: AdvancedRocketryBlocks.blockLoader (meta 1) -> Satellite Loading Hatch + * - slot 1: AdvancedRocketryItems.itemSpaceStationChip -> Station Chip (can be blank or programmed) + * Outputs (by code): + * - slot 2: AdvancedRocketryItems.itemSpaceStation -> Packed station (ItemPackedStructure) + * - slot 3: AdvancedRocketryItems.itemSpaceStationChip -> New chip ONLY when making a brand-new station + * + * We present both outputs (JEI is illustrative, not conditional). + */ +public class StationAssemblerWrapper implements IRecipeWrapper { + + private final ItemStack inputHatch; + private final ItemStack inputChip; + private final ItemStack outStation; + private final ItemStack outChipMaybe; + + public StationAssemblerWrapper() { + // Input 0: blockLoader with meta 1 + this.inputHatch = new ItemStack(AdvancedRocketryBlocks.blockLoader, 1, 1); + + // Input 1: station chip item (no NBT required for JEI showcase) + this.inputChip = new ItemStack(AdvancedRocketryItems.itemSpaceStationChip); + + // Output 2: station item + this.outStation = new ItemStack(AdvancedRocketryItems.itemSpaceStation); + + // Output 3: station chip (appears when creating a new station) + this.outChipMaybe = new ItemStack(AdvancedRocketryItems.itemSpaceStationChip); + } + + @Override + public void getIngredients(IIngredients ing) { + // --- Inputs (your two visible inputs) --- + java.util.List> inputs = new java.util.ArrayList<>(3); + inputs.add(java.util.Collections.singletonList(inputHatch)); // bay (loader meta 1) + inputs.add(java.util.Collections.singletonList(inputChip)); // empty chip + + // --- Hidden machine block for discoverability (so R/U on block opens this page) --- + ItemStack stationBlock = new ItemStack( + zmaster587.advancedRocketry.api.AdvancedRocketryBlocks.blockStationBuilder + ); + inputs.add(java.util.Collections.singletonList(stationBlock)); + + ing.setInputLists(mezz.jei.api.ingredients.VanillaTypes.ITEM, inputs); + + // --- Outputs (show both possible results of "Build") --- + java.util.List outs = new java.util.ArrayList<>(3); + outs.add(outStation); + if (outChipMaybe != null && !outChipMaybe.isEmpty()) { + outs.add(outChipMaybe); + } + + // Also include the block as an output so R on the block finds this page too + outs.add(stationBlock); + + ing.setOutputs(mezz.jei.api.ingredients.VanillaTypes.ITEM, outs); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/DataBlockProbeProvider.java b/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/DataBlockProbeProvider.java new file mode 100644 index 000000000..ccd6cec19 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/DataBlockProbeProvider.java @@ -0,0 +1,151 @@ +package zmaster587.advancedRocketry.integration.theoneprobe; + +import mcjty.theoneprobe.api.IProbeHitData; +import mcjty.theoneprobe.api.IProbeInfo; +import mcjty.theoneprobe.api.IProbeInfoProvider; +import mcjty.theoneprobe.api.NumberFormat; +import mcjty.theoneprobe.api.ProbeMode; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import zmaster587.advancedRocketry.api.DataStorage; +import zmaster587.advancedRocketry.api.DataStorage.DataType; +import zmaster587.advancedRocketry.tile.cables.TileWirelessTransciever; +import zmaster587.advancedRocketry.tile.hatch.TileDataBus; +import zmaster587.advancedRocketry.tile.satellite.TileSatelliteTerminal; +import zmaster587.libVulpes.LibVulpes; + +public class DataBlockProbeProvider implements IProbeInfoProvider { + + private static final int DATA_BORDER_COLOR = 0xFF555555; + private static final int DATA_BACKGROUND_COLOR = 0xFF000000; + private static final int DATA_FILLED_COLOR = 0xFF1FA51F; + private static final int DATA_ALT_FILLED_COLOR = 0xFF137013; + + @Override + public String getID() { + return "advancedrocketry:data_blocks"; + } + + @Override + public void addProbeInfo(ProbeMode mode, IProbeInfo probeInfo, EntityPlayer player, World world, + IBlockState blockState, IProbeHitData hitData) { + if (mode != ProbeMode.EXTENDED) { + return; + } + + TileEntity tile = world.getTileEntity(hitData.getPos()); + if (tile == null) { + return; + } + + if (tile instanceof TileWirelessTransciever) { + addWirelessDataInfo(probeInfo, (TileWirelessTransciever) tile); + return; + } + + DataStorage storage = getDataStorage(tile); + if (storage != null) { + addCommonDataInfo(probeInfo, storage, true); + } + } + + private static DataStorage getDataStorage(TileEntity tile) { + if (tile instanceof TileDataBus) { + return ((TileDataBus) tile).getDataObject(); + } + + if (tile instanceof TileSatelliteTerminal) { + return ((TileSatelliteTerminal) tile).getDataObject(); + } + + return null; + } + private static String getModeTextPadded(boolean extractMode) { + String mode = tr(extractMode + ? "msg.top.advancedrocketry.data.mode.extract" + : "msg.top.advancedrocketry.data.mode.insert"); + + if (!extractMode) { + mode += " "; + } + + return mode; + } + private static String getLinkStatusBadge(boolean linked) { + return (linked + ? net.minecraft.util.text.TextFormatting.GREEN + : net.minecraft.util.text.TextFormatting.RED) + + tr(linked + ? "msg.top.advancedrocketry.data.link.linked" + : "msg.top.advancedrocketry.data.link.unlinked"); + } + + private static void addWirelessDataInfo(IProbeInfo probeInfo, TileWirelessTransciever tile) { + DataStorage storage = tile.getUiBufferObject(); + if (storage == null) { + return; + } + + probeInfo.text( + tr("msg.top.advancedrocketry.data.mode") + + ": " + + getModeTextPadded(tile.isExtractModeWireless()) + + " " + + getLinkStatusBadge(tile.isLinkedWireless()) + ); + + addCommonDataInfo(probeInfo, storage, false); + } + + private static void addCommonDataInfo(IProbeInfo probeInfo, DataStorage storage, boolean showLockedLine) { + if (storage == null) { + return; + } + + probeInfo.text( + tr("msg.top.advancedrocketry.data.type") + + ": " + + getDataTypeText(storage.getDataType()) + ); + + addDataBar(probeInfo, storage); + + if (showLockedLine && storage.isLocked()) { + probeInfo.text(tr("msg.top.advancedrocketry.data.locked")); + } + } + + private static void addDataBar(IProbeInfo probeInfo, DataStorage storage) { + int current = storage.getData(); + int max = Math.max(1, storage.getMaxData()); + + probeInfo.progress( + current, + max, + probeInfo.defaultProgressStyle() + .borderColor(DATA_BORDER_COLOR) + .backgroundColor(DATA_BACKGROUND_COLOR) + .filledColor(DATA_FILLED_COLOR) + .alternateFilledColor(DATA_ALT_FILLED_COLOR) + .height(12) + .width(100) + .showText(true) + .numberFormat(NumberFormat.COMMAS) + .suffix(" " + tr("msg.top.advancedrocketry.data.label")) + ); + } + + private static String getDataTypeText(DataType type) { + if (type == null) { + return tr("data.undefined.name"); + } + + return tr(type.toString()); + } + + private static String tr(String key) { + return LibVulpes.proxy.getLocalizedString(key); + } +} \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/RocketEntityDisplayOverride.java b/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/RocketEntityDisplayOverride.java new file mode 100644 index 000000000..9a10881eb --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/RocketEntityDisplayOverride.java @@ -0,0 +1,49 @@ +package zmaster587.advancedRocketry.integration.theoneprobe; + +import mcjty.theoneprobe.api.IEntityDisplayOverride; +import mcjty.theoneprobe.api.IProbeHitEntityData; +import mcjty.theoneprobe.api.IProbeInfo; +import mcjty.theoneprobe.api.ProbeMode; +import mcjty.theoneprobe.api.TextStyleClass; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.world.World; +import zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType; +import zmaster587.advancedRocketry.entity.EntityRocket; +import zmaster587.libVulpes.LibVulpes; + +public class RocketEntityDisplayOverride implements IEntityDisplayOverride { + + @Override + public boolean overrideStandardInfo(ProbeMode mode, IProbeInfo probeInfo, + EntityPlayer player, World world, + Entity entity, IProbeHitEntityData data) { + if (!(entity instanceof EntityRocket)) { + return false; + } + + EntityRocket rocket = (EntityRocket) entity; + probeInfo.text(TextStyleClass.NAME + getRocketDisplayName(rocket)); + probeInfo.text(TextStyleClass.MODNAME + "Advanced Rocketry"); + return true; + } + + private static String tr(String key) { + return LibVulpes.proxy.getLocalizedString(key); + } + + private static String getRocketDisplayName(EntityRocket rocket) { + FuelType mainFuel = rocket.getRocketFuelType(); + + if (mainFuel == FuelType.LIQUID_MONOPROPELLANT) { + return tr("msg.top.advancedrocketry.rocket.monopropellant"); + } + if (mainFuel == FuelType.LIQUID_BIPROPELLANT) { + return tr("msg.top.advancedrocketry.rocket.bipropellant"); + } + if (mainFuel == FuelType.NUCLEAR_WORKING_FLUID) { + return tr("msg.top.advancedrocketry.rocket.nuclear"); + } + return tr("entity.advancedrocketry.rocket.name"); + } +} \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/RocketEntityProbeProvider.java b/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/RocketEntityProbeProvider.java new file mode 100644 index 000000000..834c7feef --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/RocketEntityProbeProvider.java @@ -0,0 +1,319 @@ +package zmaster587.advancedRocketry.integration.theoneprobe; + +import mcjty.theoneprobe.api.IProbeHitEntityData; +import mcjty.theoneprobe.api.IProbeInfo; +import mcjty.theoneprobe.api.IProbeInfoEntityProvider; +import mcjty.theoneprobe.api.NumberFormat; +import mcjty.theoneprobe.api.ProbeMode; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import zmaster587.advancedRocketry.api.ARConfiguration; +import zmaster587.advancedRocketry.api.Constants; +import zmaster587.advancedRocketry.api.StatsRocket; +import zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType; +import zmaster587.advancedRocketry.api.stations.ISpaceObject; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.entity.EntityRocket; +import zmaster587.advancedRocketry.item.ItemAsteroidChip; +import zmaster587.advancedRocketry.item.ItemPlanetIdentificationChip; +import zmaster587.advancedRocketry.item.ItemStationChip; +import zmaster587.advancedRocketry.stations.SpaceObjectManager; +import zmaster587.advancedRocketry.tile.TileGuidanceComputer; +import zmaster587.advancedRocketry.util.StationLandingLocation; +import zmaster587.libVulpes.items.ItemLinker; +import zmaster587.libVulpes.LibVulpes; +import zmaster587.libVulpes.util.Vector3F; + +import java.util.Locale; + +public class RocketEntityProbeProvider implements IProbeInfoEntityProvider { + + private static final int FUEL_BORDER_COLOR = 0xFF555555; + private static final int FUEL_BACKGROUND_COLOR = 0xFF000000; + private static final int FUEL_FILLED_COLOR = 0xFF284892; + private static final int FUEL_ALT_FILLED_COLOR = 0xFF162F69; + + @Override + public String getID() { + return "advancedrocketry:rocket_entity"; + } + + @Override + public void addProbeEntityInfo(ProbeMode mode, IProbeInfo probeInfo, EntityPlayer player, World world, Entity entity, IProbeHitEntityData data) { + if (!(entity instanceof EntityRocket)) { + return; + } + + EntityRocket rocket = (EntityRocket) entity; + StatsRocket stats = rocket.getRocketStats(); + + addGuidanceInfo(probeInfo, rocket); + + if (mode == ProbeMode.EXTENDED) { + addFuelInfo(probeInfo, rocket, stats); + } + } + + private static String tr(String key) { + return LibVulpes.proxy.getLocalizedString(key); + } + + private static String trf(String key, Object... args) { + return new TextComponentTranslation(key, args).getUnformattedText(); + } + + private static void addGuidanceInfo(IProbeInfo probeInfo, EntityRocket rocket) { + if (rocket.storage == null) { + return; + } + + TileGuidanceComputer gc = rocket.storage.getGuidanceComputer(); + if (gc == null) { + probeInfo.text(tr("msg.top.advancedrocketry.guidance.noComputer")); + return; + } + + ItemStack stack = gc.getStackInSlot(0); + if (stack.isEmpty()) { + probeInfo.text(tr("msg.top.advancedrocketry.guidance.noDestination")); + return; + } + + String primaryText = getGuidancePrimaryText(rocket, gc, stack); + + IProbeInfo row = probeInfo.horizontal(); + row.item(stack, probeInfo.defaultItemStyle().width(16).height(16)); + row.text(primaryText); + } + + private static String getGuidancePrimaryText(EntityRocket rocket, TileGuidanceComputer gc, ItemStack stack) { + if (stack.getItem() instanceof ItemAsteroidChip) { + ItemAsteroidChip chip = (ItemAsteroidChip) stack.getItem(); + String type = chip.getType(stack); + Long uuid = chip.getUUID(stack); + + if (uuid == null || type == null || type.isEmpty()) { + return tr("msg.top.advancedrocketry.guidance.unprogrammed"); + } + + return trf( + "msg.top.advancedrocketry.guidance.asteroidWithId", + type, + ItemAsteroidChip.shortDisplayId(uuid, type) + ); + } + + if (stack.getItem() instanceof ItemStationChip) { + int stationId = ItemStationChip.getUUID(stack); + if (stationId == 0) { + return tr("msg.top.advancedrocketry.guidance.unprogrammed"); + } + return trf("msg.top.advancedrocketry.guidance.station", stationId); + } + + if (stack.getItem() instanceof ItemPlanetIdentificationChip) { + ItemPlanetIdentificationChip chip = (ItemPlanetIdentificationChip) stack.getItem(); + + if (!chip.hasValidDimension(stack)) { + return tr("msg.top.advancedrocketry.guidance.unprogrammed"); + } + + if (chip.getDimensionProperties(stack) == null) { + return tr("msg.top.advancedrocketry.guidance.unprogrammed"); + } + + return chip.getDimensionProperties(stack).getName(); + } + + if (isLinker(stack)) { + if (isUnprogrammedLinker(stack)) { + return tr("msg.top.advancedrocketry.guidance.unprogrammed"); + } + return getCurrentLaunchDestinationText(rocket, gc, stack); + } + + String resolved = getCurrentLaunchDestinationText(rocket, gc, stack); + if (resolved.equals(tr("msg.top.advancedrocketry.guidance.unprogrammed"))) { + return resolved; + } + + return stripTrailingCoords(resolved); + } + + private static String getCurrentLaunchDestinationText(EntityRocket rocket, TileGuidanceComputer gc, ItemStack stack) { + int currentDim = rocket.world.provider.getDimension(); + int destDim = rocket.storage.getDestinationDimId(currentDim, (int) rocket.posX, (int) rocket.posZ); + + if (stack.isEmpty() + && ARConfiguration.getCurrentConfig().experimentalSpaceFlight + && destDim != Constants.INVALID_PLANET) { + return tr("msg.top.advancedrocketry.guidance.orbit"); + } + + if (destDim == Constants.INVALID_PLANET || destDim == SpaceObjectManager.WARPDIMID) { + return tr("msg.top.advancedrocketry.guidance.unprogrammed"); + } + + if (stack.getItem() instanceof ItemStationChip + && destDim == ARConfiguration.getCurrentConfig().spaceDimId) { + int stationId = ItemStationChip.getUUID(stack); + if (stationId != 0) { + return trf("msg.top.advancedrocketry.guidance.station", stationId); + } + return tr("msg.top.advancedrocketry.guidance.unprogrammed"); + } + + Vector3F loc = rocket.storage.getDestinationCoordinates(destDim, false); + + if (destDim == ARConfiguration.getCurrentConfig().spaceDimId) { + if (loc != null) { + ISpaceObject station = SpaceObjectManager.getSpaceManager() + .getSpaceStationFromBlockCoords(new BlockPos(loc.x, loc.y, loc.z)); + + if (station != null) { + String text = trf("msg.top.advancedrocketry.guidance.station", station.getId()); + + StationLandingLocation pad = gc.getLandingLocation(station.getId()); + if (pad != null) { + text += trf("msg.top.advancedrocketry.guidance.pad", pad); + } + + return text; + } + } + + return tr("msg.top.advancedrocketry.guidance.space"); + } + + String text = DimensionManager.getInstance().getDimensionProperties(destDim).getName(); + + String name = gc.getDestinationName(destDim); + if (!name.isEmpty()) { + text += " - " + name; + } + + if (loc != null) { + text += String.format(Locale.ROOT, " (%.0f, %.0f)", loc.x, loc.z); + } + + return text; + } + + + + private static boolean isLinker(ItemStack stack) { + return !stack.isEmpty() && stack.getItem() instanceof ItemLinker; + } + + private static boolean isUnprogrammedLinker(ItemStack stack) { + return isLinker(stack) && !ItemLinker.isSet(stack); + } + + private static String stripTrailingCoords(String text) { + if (text == null || text.isEmpty()) { + return text; + } + return text.replaceAll("\\s*\\((-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)\\)$", ""); + } + private static void addFuelInfo(IProbeInfo probeInfo, EntityRocket rocket, StatsRocket stats) { + FuelType mainFuel = rocket.getRocketFuelType(); + if (mainFuel == null) { + return; + } + + switch (mainFuel) { + case LIQUID_MONOPROPELLANT: + addFuelSection( + probeInfo, + tr("msg.top.advancedrocketry.fuel.label"), + stats.getFuelFluid(), + rocket.getFuelAmount(FuelType.LIQUID_MONOPROPELLANT), + rocket.getFuelCapacity(FuelType.LIQUID_MONOPROPELLANT) + ); + break; + + case LIQUID_BIPROPELLANT: + addFuelSection( + probeInfo, + tr("msg.top.advancedrocketry.fuel.label"), + stats.getFuelFluid(), + rocket.getFuelAmount(FuelType.LIQUID_BIPROPELLANT), + rocket.getFuelCapacity(FuelType.LIQUID_BIPROPELLANT) + ); + + addFuelSection( + probeInfo, + tr("msg.top.advancedrocketry.fuel.oxidizer"), + stats.getOxidizerFluid(), + rocket.getFuelAmount(FuelType.LIQUID_OXIDIZER), + rocket.getFuelCapacity(FuelType.LIQUID_OXIDIZER) + ); + break; + + case NUCLEAR_WORKING_FLUID: + addFuelSection( + probeInfo, + tr("msg.top.advancedrocketry.fuel.workingFluid"), + stats.getWorkingFluid(), + rocket.getFuelAmount(FuelType.NUCLEAR_WORKING_FLUID), + rocket.getFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID) + ); + break; + } + } + + private static void addFuelSection(IProbeInfo probeInfo, String label, String registryName, int amount, int capacity) { + if (capacity <= 0) { + return; + } + + String fluidDisplayName = getPrettyFluidName(registryName); + + if (fluidDisplayName != null) { + probeInfo.text(label + ": " + fluidDisplayName); + } else if (amount > 0) { + probeInfo.text(label + ": " + tr("msg.top.advancedrocketry.fuel.unknownFluid")); + } else { + probeInfo.text(label + ":"); + } + + probeInfo.progress( + amount, + capacity, + probeInfo.defaultProgressStyle() + .borderColor(FUEL_BORDER_COLOR) + .backgroundColor(FUEL_BACKGROUND_COLOR) + .filledColor(FUEL_FILLED_COLOR) + .alternateFilledColor(FUEL_ALT_FILLED_COLOR) + .height(12) + .width(100) + .showText(true) + .suffix(" mB") + .numberFormat(NumberFormat.COMMAS) + ); + } + + private static String getPrettyFluidName(String registryName) { + if (registryName == null || registryName.isEmpty() || "null".equals(registryName)) { + return null; + } + + Fluid fluid = FluidRegistry.getFluid(registryName); + if (fluid == null) { + return registryName; + } + + try { + return fluid.getLocalizedName(new FluidStack(fluid, 1)); + } catch (Exception e) { + return fluid.getName(); + } + } +} \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/TopIntegration.java b/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/TopIntegration.java new file mode 100644 index 000000000..4b02110c0 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/integration/theoneprobe/TopIntegration.java @@ -0,0 +1,36 @@ +package zmaster587.advancedRocketry.integration.theoneprobe; + +import mcjty.theoneprobe.api.ITheOneProbe; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.event.FMLInterModComms; + +import javax.annotation.Nullable; +import java.util.function.Function; + +public class TopIntegration { + + private TopIntegration() {} + + public static void register() { + if (!Loader.isModLoaded("theoneprobe")) { + return; + } + + FMLInterModComms.sendFunctionMessage( + "theoneprobe", + "getTheOneProbe", + "zmaster587.advancedRocketry.integration.theoneprobe.TopIntegration$GetTheOneProbe" + ); + } + + public static class GetTheOneProbe implements Function { + @Nullable + @Override + public Void apply(ITheOneProbe top) { + top.registerEntityDisplayOverride(new RocketEntityDisplayOverride()); + top.registerEntityProvider(new RocketEntityProbeProvider()); + top.registerProvider(new DataBlockProbeProvider()); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleAutoData.java b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleAutoData.java index a1752b9f6..d1969b9c2 100644 --- a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleAutoData.java +++ b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleAutoData.java @@ -104,8 +104,9 @@ public void renderForeground(int guiOffsetX, int guiOffsetY, int mouseX, int mou } List list = new LinkedList<>(); - list.add(totalData + " / " + totalMaxData + " Data"); - list.add("Type: " + I18n.format(data[0].getDataType().toString())); + list.add(totalData + " / " + totalMaxData + " " + I18n.format("data.label.data")); + list.add(I18n.format("data.label.type") + " " + I18n.format(data[0].getDataType().toString())); + this.drawTooltip(gui, list, mouseX, mouseY, zLevel, font); } diff --git a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleContainerPanYOnlyWithScrollCache.java b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleContainerPanYOnlyWithScrollCache.java new file mode 100644 index 000000000..eb948a4a9 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleContainerPanYOnlyWithScrollCache.java @@ -0,0 +1,281 @@ +package zmaster587.advancedRocketry.inventory.modules; + +import zmaster587.libVulpes.inventory.modules.ModuleContainerPanYOnly; +import zmaster587.libVulpes.inventory.modules.ModuleBase; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.event.GuiScreenEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; + +@SideOnly(Side.CLIENT) +public class ModuleContainerPanYOnlyWithScrollCache extends ModuleContainerPanYOnly { + + // ===== Single-slot global cache (latest write wins; no memory growth) ===== + private static final int NO_SCROLL = Integer.MIN_VALUE; + private static final AtomicInteger GLOBAL_SCROLL = new AtomicInteger(NO_SCROLL); + + // ===== Track live instances so the event handler can find "this" without editing GuiModular ===== + private static final CopyOnWriteArrayList> LIVE = + new CopyOnWriteArrayList<>(); + + @SideOnly(Side.CLIENT) + private WeakReference lastGui = new WeakReference<>(null); + + @SideOnly(Side.CLIENT) + private static volatile boolean EVENT_REGISTERED = false; + + // >>> Store GUI origin captured during render to avoid touching protected xSize/ySize + @SideOnly(Side.CLIENT) + private volatile int lastGuiLeft = 0, lastGuiTop = 0; + + // ===== Instance state ===== + private int lastSavedY = NO_SCROLL; + private boolean didRestore = false; + + // Debounce to avoid micro-stutter under heavy input + private long lastSaveNs = 0L; + private static final long SAVE_INTERVAL_NS = 30_000_000L; // 30 ms + + public ModuleContainerPanYOnlyWithScrollCache( + int offsetX, int offsetY, + List moduleList, List staticModules, + ResourceLocation backdrop, + int screenSizeX, int screenSizeY, + int paddingX, int paddingY, + int containerSizeX, int containerSizeY + ) { + super(offsetX, offsetY, moduleList, staticModules, backdrop, + screenSizeX, screenSizeY, paddingX, paddingY, + containerSizeX, containerSizeY); + + // Register this instance for event routing (client-side only) + if (Minecraft.getMinecraft() != null) { + LIVE.add(new WeakReference<>(this)); + maybeRegisterEventHandler(); + } + } + + // Ensure we don’t leak refs if the module gets disabled/detached + @Override + public void setEnabled(boolean state) { + if (!state && this.isEnabled()) { + saveScrollIfChangedForce(); // persist last position + } + super.setEnabled(state); + // Optional: prune dead refs occasionally + pruneDeadRefs(); + } + + // Clamp to base’s legal range [-containerSizeY, 0] + private int clampScroll(int y) { + if (y > 0) return 0; + int min = -this.containerSizeY; + return (y < min) ? min : y; + } + + // Debounced save after movement, skipping no-op writes globally and per-instance + private void saveScrollIfChanged() { + final int y = clampScroll(super.getScrollY()); + + // Per-instance no-op: if we already observed y, don't do anything. + if (y == lastSavedY) return; + + // Global no-op: if the global cache already holds y, skip the write, but + // update our lastSavedY so we don't keep re-checking. + final int global = GLOBAL_SCROLL.get(); + if (global == y) { + lastSavedY = y; + return; + } + + // Keep debounce to avoid bursts during fine-grained drags. + final long now = System.nanoTime(); + if (now - lastSaveNs < SAVE_INTERVAL_NS) { + lastSavedY = y; // remember the new y even if we didn't write globally yet + return; + } + + lastSaveNs = now; + lastSavedY = y; + + // Use lazySet for cheap release write, perfectly fine for a UI cache + GLOBAL_SCROLL.lazySet(y); + + // DEBUG + //System.out.println("[SCROLLER] save y=" + y); + } + + + // Force save (bypass debounce) on close or disable, still skipping no-op + private void saveScrollIfChangedForce() { + final int y = clampScroll(super.getScrollY()); + + // If both our last and the global already equal y, it's a no-op + if (y == lastSavedY && GLOBAL_SCROLL.get() == y) return; + + lastSavedY = y; + lastSaveNs = System.nanoTime(); + + GLOBAL_SCROLL.lazySet(y); + + // DEBUG + //System.out.println("[SCROLLER] force-save y=" + y); + } + + // Restore once when bounds are stable + @Override + @SideOnly(Side.CLIENT) + public void renderBackground(GuiContainer gui, int x, int y, int mouseX, int mouseY, FontRenderer font) { + // Remember the GUI we’re rendering in so the event handler can filter by current screen + this.lastGui = new WeakReference<>(gui); + + // >>> Capture guiLeft/guiTop from the parameters + this.lastGuiLeft = x; + this.lastGuiTop = y; + + if (!didRestore) { + int v = GLOBAL_SCROLL.get(); + if (v != NO_SCROLL) { + int clamped = clampScroll(v); + super.setOffset2(-clamped); // base uses -y + lastSavedY = clamped; + //System.out.println("[SCROLLER] restore y=" + clamped); // DEBUG + } + didRestore = true; + } + super.renderBackground(gui, x, y, mouseX, mouseY, font); + } + + + @Override + @SideOnly(Side.CLIENT) + public void renderForeground(int guiOffsetX, int guiOffsetY, int mouseX, int mouseY, float zLevel, + GuiContainer gui, FontRenderer font) { + super.renderForeground(guiOffsetX, guiOffsetY, mouseX, mouseY, zLevel, gui, font); + } + + // Single save point for any movement + @Override + protected void moveContainerInterior(int deltaY) { + super.moveContainerInterior(deltaY); + saveScrollIfChanged(); + } + + // Base onScroll calls moveContainerInterior; don’t double-save here + @Override + public void onScroll(int dwheel) { + super.onScroll(dwheel); + } + + @Override + @SideOnly(Side.CLIENT) + public void onMouseClickedAndDragged(int x, int y, int button, long timeSinceLastClick) { + super.onMouseClickedAndDragged(x, y, button, timeSinceLastClick); + } + + // Public clear (e.g., on new scan) + public static void clearScrollCache() { + GLOBAL_SCROLL.set(NO_SCROLL); + //System.out.println("[SCROLLER] clear cache"); // DEBUG + } + + // ===== Event routing (client-side only) ===== + + @SideOnly(Side.CLIENT) + private static void maybeRegisterEventHandler() { + if (!EVENT_REGISTERED) { + MinecraftForge.EVENT_BUS.register(new WheelRouter()); + EVENT_REGISTERED = true; + } + } + + @SideOnly(Side.CLIENT) + private static void pruneDeadRefs() { + for (WeakReference ref : LIVE) { + if (ref.get() == null) LIVE.remove(ref); + } + } + + @SideOnly(Side.CLIENT) + private boolean isMouseOverThis(int relX, int relY) { + // relX/relY are GUI-relative to (guiLeft, guiTop) + int localX = relX - this.offsetX; + int localY = relY - this.offsetY; + return localX >= 0 && localX < this.screenSizeX + && localY >= 0 && localY < this.screenSizeY; + } + + @SideOnly(Side.CLIENT) + private boolean isOnThisGui(GuiScreen current) { + GuiContainer g = lastGui.get(); + return g != null && g == current; + } + + @SideOnly(Side.CLIENT) + private static class WheelRouter { + private static int lastTickDispatched = -1; + private static int lastScreenId = 0; + private static int lastWheelSign = 0; // -1/+1 + + @SubscribeEvent + public void onMouseInputPre(GuiScreenEvent.MouseInputEvent.Pre evt) throws IOException { + GuiScreen screen = evt.getGui(); + if (!(screen instanceof GuiContainer)) return; + + int d = org.lwjgl.input.Mouse.getEventDWheel(); + if (d == 0) return; + + Minecraft mc = Minecraft.getMinecraft(); + int tick = (mc.ingameGUI != null) ? mc.ingameGUI.getUpdateCounter() : 0; + int screenId = System.identityHashCode(screen); + int sign = Integer.signum(d); + + // Coalesce: same screen + same tick + same direction => treat as duplicate + if (tick == lastTickDispatched && screenId == lastScreenId && sign == lastWheelSign) { + evt.setCanceled(true); + return; + } + + int scaledW = screen.width, scaledH = screen.height; + int mouseX = org.lwjgl.input.Mouse.getX() * scaledW / mc.displayWidth; + int mouseY = scaledH - org.lwjgl.input.Mouse.getY() * scaledH / mc.displayHeight - 1; + + boolean handled = false; + for (int i = LIVE.size() - 1; i >= 0; i--) { + WeakReference ref = LIVE.get(i); + ModuleContainerPanYOnlyWithScrollCache mod = ref.get(); + if (mod == null) { LIVE.remove(i); continue; } + if (!mod.getVisible() || !mod.isEnabled()) continue; + if (!mod.isOnThisGui(screen)) continue; + + int relX = mouseX - mod.lastGuiLeft; + int relY = mouseY - mod.lastGuiTop; + if (!mod.isMouseOverThis(relX, relY)) continue; + + mod.onScroll(d); // will call moveContainerInterior -> save + handled = true; + break; + } + + if (handled) { + lastTickDispatched = tick; + lastScreenId = screenId; + lastWheelSign = sign; + evt.setCanceled(true); + } + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleData.java b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleData.java index f6becbf65..a2bd48f74 100644 --- a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleData.java +++ b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleData.java @@ -132,8 +132,9 @@ public void renderForeground(int guiOffsetX, int guiOffsetY, int mouseX, int mou } List list = new LinkedList<>(); - list.add(totalData + " / " + totalMaxData + " Data"); - list.add("Type: " + I18n.format(data[0].getDataType().toString())); + list.add(totalData + " / " + totalMaxData + " " + I18n.format("data.label.data")); + list.add(I18n.format("data.label.type") + " " + I18n.format(data[0].getDataType().toString())); + this.drawTooltip(gui, list, mouseX, mouseY, zLevel, font); } diff --git a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleItemSlotButton.java b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleItemSlotButton.java new file mode 100644 index 000000000..d7701cf42 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleItemSlotButton.java @@ -0,0 +1,66 @@ +package zmaster587.advancedRocketry.inventory.modules; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.libVulpes.gui.CommonResources; +import zmaster587.libVulpes.inventory.TextureResources; +import zmaster587.libVulpes.inventory.modules.IButtonInventory; +import zmaster587.libVulpes.inventory.modules.ModuleButton; + +import javax.annotation.Nonnull; + +/** + * Slot-like clickable button that renders ANY ItemStack (including non-block items) using RenderItem. + * Drop-in replacement for ModuleSlotButton when the stack is not a Block item. + * zmaster587.libVulpes.inventory.modules.ModuleSlotButton only works for Block items. + * this class was created to allow displaying items such as batteries, ingots, etc. + * if libvulpes ModuleSlotButton is updated to support non-block items, this class may be deprecated. + * remove this class if libvulpes ModuleSlotButton is updated to support non-block items. + */ +public class ModuleItemSlotButton extends ModuleButton { + + private final ItemStack stack; + + public ModuleItemSlotButton(int offsetX, int offsetY, int buttonId, IButtonInventory tile, + @Nonnull ItemStack slotDisplay, String extraDisplay) { + // IMPORTANT: pass "" as button label so nothing is drawn + super(offsetX, offsetY, buttonId, "", tile, + TextureResources.buttonNull, + "", // <- was: slotDisplay.getDisplayName() + "\n" + extraDisplay + 16, 16); + + this.stack = slotDisplay; + + // Set tooltip instead (hover-only) + String tt = slotDisplay.isEmpty() ? "" : slotDisplay.getDisplayName(); + if (extraDisplay != null && !extraDisplay.isEmpty()) { + tt = tt.isEmpty() ? extraDisplay : (tt + " \n" + extraDisplay); + } + this.setToolTipText(tt); + } + + @SideOnly(Side.CLIENT) + @Override + public void renderBackground(GuiContainer gui, int x, int y, int mouseX, int mouseY, FontRenderer font) { + Minecraft.getMinecraft().getTextureManager().bindTexture(CommonResources.genericBackground); + gui.drawTexturedModalRect(x + this.offsetX - 1, y + this.offsetY - 1, 176, 0, 18, 18); + + if (stack.isEmpty()) return; + + int ix = x + this.offsetX; + int iy = y + this.offsetY; + + Minecraft mc = Minecraft.getMinecraft(); + + RenderHelper.enableGUIStandardItemLighting(); + mc.getRenderItem().renderItemAndEffectIntoGUI(stack, ix, iy); + //mc.getRenderItem().renderItemOverlayIntoGUI(font, stack, ix, iy, null); + RenderHelper.disableStandardItemLighting(); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModulePlanetSelector.java b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModulePlanetSelector.java index 41380acbe..24a192f66 100644 --- a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModulePlanetSelector.java +++ b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModulePlanetSelector.java @@ -12,6 +12,7 @@ import net.minecraft.inventory.IContainerListener; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.text.translation.I18n; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -49,14 +50,48 @@ public class ModulePlanetSelector extends ModuleContainerPan implements IButtonI private PlanetRenderProperties currentlySelectedPlanet; private IPlanetDefiner planetDefiner; private int currentlySelectedPlanetID = -1; + + private IProgressBar progressSource; + + private static final IProgressBar NULL_PROGRESS = new IProgressBar() { + @Override public float getNormallizedProgress(int id) { return 0f; } + @Override public void setProgress(int id, int progress) {} + @Override public int getProgress(int id) { return 0; } + @Override public int getTotalProgress(int id) { return 1; } + @Override public void setTotalProgress(int id, int progress) {} + }; + + public ModulePlanetSelector(int planetId, ResourceLocation backdrop, ISelectionNotify tile, boolean star) { - this(planetId, backdrop, tile, null, star); + this(planetId, backdrop, tile, null, null, star); } public ModulePlanetSelector(int planetId, ResourceLocation backdrop, ISelectionNotify tile, IPlanetDefiner definer, boolean star) { + this(planetId, backdrop, tile, null, definer, star); + } + + // NEW overloads for Option A + public ModulePlanetSelector(int planetId, ResourceLocation backdrop, ISelectionNotify tile, + IProgressBar progress, boolean star) { + this(planetId, backdrop, tile, progress, null, star); + } + + public ModulePlanetSelector(int planetId, ResourceLocation backdrop, ISelectionNotify tile, + IProgressBar progress, IPlanetDefiner definer, boolean star) { super(0, 0, null, null, backdrop, 0, 0, 0, 0, size, size); + this.planetDefiner = definer; - hostTile = tile; + this.hostTile = tile; + + // choose progress provider safely + if (progress != null) { + this.progressSource = progress; + } else if (tile instanceof IProgressBar) { + this.progressSource = (IProgressBar) tile; + } else { + this.progressSource = NULL_PROGRESS; + } + int center = size / 2; zoom = 1.0; @@ -69,20 +104,37 @@ public ModulePlanetSelector(int planetId, ResourceLocation backdrop, ISelectionN selectedSystem = Constants.INVALID_PLANET; stellarView = false; - staticModuleList.add(new ModuleButton(0, 0, Constants.INVALID_PLANET, "<< Up", this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); - staticModuleList.add(new ModuleButton(0, 18, Constants.INVALID_PLANET + 1, "Select", this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); - staticModuleList.add(new ModuleButton(0, 36, Constants.INVALID_PLANET + 2, "PlanetList", this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); + staticModuleList.add(new ModuleButton(0, 0, Constants.INVALID_PLANET, + I18n.translateToLocal("msg.advancedrocketry.planetselector.up"), + this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); + + staticModuleList.add(new ModuleButton(0, 18, Constants.INVALID_PLANET + 1, + I18n.translateToLocal("msg.advancedrocketry.planetselector.select"), + this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); + + staticModuleList.add(new ModuleButton(0, 36, Constants.INVALID_PLANET + 2, + I18n.translateToLocal("msg.advancedrocketry.planetselector.planet.list"), + this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); + ModuleDualProgressBar progressBar; - staticModuleList.add(progressBar = new ModuleDualProgressBar(100, 0, 0, TextureResources.atmIndicator, (IProgressBar) tile, "%b -> %a Earth's atmospheric pressure")); + + staticModuleList.add(progressBar = new ModuleDualProgressBar(100, 0, 0, + TextureResources.atmIndicator, progressSource, + net.minecraft.util.text.translation.I18n.translateToLocal("msg.advancedrocketry.planetselector.atm.tooltip"))); progressBar.setTooltipValueMultiplier(.16f); - staticModuleList.add(progressBar = new ModuleDualProgressBar(200, 0, 2, TextureResources.massIndicator, (IProgressBar) tile, "%b -> %a Earth's mass")); + staticModuleList.add(progressBar = new ModuleDualProgressBar(200, 0, 2, + TextureResources.massIndicator, progressSource, + net.minecraft.util.text.translation.I18n.translateToLocal("msg.advancedrocketry.planetselector.mass.tooltip"))); progressBar.setTooltipValueMultiplier(.02f); - staticModuleList.add(progressBar = new ModuleDualProgressBar(300, 0, 1, TextureResources.distanceIndicator, (IProgressBar) tile, "%b -> %a Relative Distance units")); + staticModuleList.add(progressBar = new ModuleDualProgressBar(300, 0, 1, + TextureResources.distanceIndicator, progressSource, + net.minecraft.util.text.translation.I18n.translateToLocal("msg.advancedrocketry.planetselector.distance.tooltip"))); progressBar.setTooltipValueMultiplier(.16f); + //renderPlanetarySystem(properties, center, center, 3f); if (FMLCommonHandler.instance().getSide().isClient()) { @@ -160,15 +212,36 @@ private void renderGalaxyMap(IGalaxy galaxy, int posX, int posY, float distanceZ deltaX = (int) ((int) (star2.getStarSeparation() * MathHelper.cos(phase) * 0.5*distanceZoomMultiplier)); deltaY = (int) ((int) (star2.getStarSeparation() * MathHelper.sin(phase) * 0.5*distanceZoomMultiplier)); - planetList.add(button = new ModuleButton(offsetX + deltaX, offsetY + deltaY, star2.getId() + Constants.STAR_ID_OFFSET, "", this, new ResourceLocation[]{star2.isBlackHole() ? TextureResources.locationBlackHole_icon : TextureResources.locationSunNew}, String.format("Name: %s\nNumber of Planets: %d", star2.getName(), star.getNumPlanets()), displaySize, displaySize)); + planetList.add(button = new ModuleButton( + offsetX + deltaX, + offsetY + deltaY, + star2.getId() + Constants.STAR_ID_OFFSET, + "", + this, + new ResourceLocation[]{star2.isBlackHole() ? TextureResources.locationBlackHole_icon : TextureResources.locationSunNew}, + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.star.tooltip.name", star2.getName()) + + "\n" + + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.star.tooltip.number.of.planets", star.getNumPlanets()), + displaySize, + displaySize)); button.setSound("buttonBlipA"); button.setBGColor(star2.getColorRGB8()); phase += phaseInc; } } - planetList.add(button = new ModuleButton(offsetX, offsetY, star.getId() + Constants.STAR_ID_OFFSET, "", this, new ResourceLocation[]{star.isBlackHole() ? TextureResources.locationBlackHole_icon : TextureResources.locationSunNew}, String.format("Name: %s\nNumber of Planets: %d", star.getName(), star.getNumPlanets()), displaySize, displaySize)); - + planetList.add(button = new ModuleButton( + offsetX, offsetY, + star.getId() + Constants.STAR_ID_OFFSET, + "", + this, + new ResourceLocation[]{star.isBlackHole() ? TextureResources.locationBlackHole_icon : TextureResources.locationSunNew}, + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.star.tooltip.name", star.getName()) + + "\n" + + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.star.tooltip.number.of.planets", star.getNumPlanets()), + displaySize, displaySize)); + + button.setSound("buttonBlipA"); button.setBGColor(star.getColorRGB8()); @@ -200,7 +273,18 @@ private void renderStarSystem(StellarBody star, int posX, int posY, float distan deltaX = (int) (star2.getStarSeparation() * MathHelper.cos(phase) * 0.5); deltaY = (int) (star2.getStarSeparation() * MathHelper.sin(phase) * 0.5); - planetList.add(button = new ModuleButton(offsetX + deltaX, offsetY + deltaY, star2.getId() + Constants.STAR_ID_OFFSET, "", this, new ResourceLocation[]{star2.isBlackHole() ? TextureResources.locationBlackHole_icon : TextureResources.locationSunNew}, String.format("Name: %s\nNumber of Planets: %d", star2.getName(), star.getNumPlanets()), displaySize, displaySize)); + planetList.add(button = new ModuleButton( + offsetX + deltaX, offsetY + deltaY, + star2.getId() + Constants.STAR_ID_OFFSET, + "", + this, + new ResourceLocation[]{star2.isBlackHole() ? TextureResources.locationBlackHole_icon : TextureResources.locationSunNew}, + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.star.tooltip.name", star2.getName()) + + "\n" + + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.star.tooltip.number.of.planets", star.getNumPlanets()), + displaySize, displaySize + )); + button.setSound("buttonBlipA"); button.setBGColor(star2.getColorRGB8()); phase += phaseInc; @@ -210,7 +294,18 @@ private void renderStarSystem(StellarBody star, int posX, int posY, float distan offsetX = posX - displaySize / 2; offsetY = posY - displaySize / 2; - planetList.add(button = new ModuleButton(offsetX, offsetY, star.getId() + Constants.STAR_ID_OFFSET, "", this, new ResourceLocation[]{star.isBlackHole() ? TextureResources.locationBlackHole_icon : TextureResources.locationSunNew}, String.format("Name: %s\nNumber of Planets: %d", star.getName(), star.getNumPlanets()), displaySize, displaySize)); + planetList.add(button = new ModuleButton( + offsetX, offsetY, + star.getId() + Constants.STAR_ID_OFFSET, + "", + this, + new ResourceLocation[]{star.isBlackHole() ? TextureResources.locationBlackHole_icon : TextureResources.locationSunNew}, + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.star.tooltip.name", star.getName()) + + "\n" + + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.star.tooltip.number.of.planets", star.getNumPlanets()), + displaySize, displaySize + )); + button.setSound("buttonBlipA"); button.setBGColor(star.getColorRGB8()); renderPropertiesMap.put(star.getId() + Constants.STAR_ID_OFFSET, new PlanetRenderProperties(displaySize, offsetX, offsetY)); @@ -279,7 +374,12 @@ private void renderPlanets(DimensionProperties planet, int parentOffsetX, int pa ModuleButton button; - planetList.add(button = new ModuleButtonPlanet(offsetX, offsetY, planet.getId(), "", this, planet, planet.getName() + "\nMoons: " + planet.getChildPlanets().size(), displaySize, displaySize)); + planetList.add(button = new ModuleButtonPlanet( + offsetX, offsetY, planet.getId(), "", this, planet, + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.planet.tooltip.name", planet.getName()) + + "\n" + + I18n.translateToLocalFormatted("msg.advancedrocketry.planetselector.planet.tooltip.moons.count", planet.getChildPlanets().size()), + displaySize, displaySize)); button.setSound("buttonBlipA"); renderPropertiesMap.put(planet.getId(), new PlanetRenderProperties(displaySize, offsetX, offsetY)); @@ -403,6 +503,7 @@ public void onMouseClicked(GuiModular gui, int x, int y, int button) { } @Override + @SideOnly(Side.CLIENT) public void renderForeground(int guiOffsetX, int guiOffsetY, int mouseX, int mouseY, float zLevel, GuiContainer gui, FontRenderer font) { super.renderForeground(guiOffsetX, guiOffsetY, mouseX, mouseY, zLevel, gui, diff --git a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleSatelliteTerminal.java b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleSatelliteTerminal.java new file mode 100644 index 000000000..3a11af468 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleSatelliteTerminal.java @@ -0,0 +1,230 @@ +package zmaster587.advancedRocketry.inventory.modules; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IContainerListener; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +import zmaster587.advancedRocketry.tile.satellite.TileSatelliteTerminal; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.util.PlanetaryTravelHelper; +import zmaster587.advancedRocketry.satellite.SatelliteData; +import zmaster587.advancedRocketry.api.satellite.SatelliteBase; +import zmaster587.advancedRocketry.item.ItemSatelliteIdentificationChip; + +import zmaster587.libVulpes.LibVulpes; +import zmaster587.libVulpes.inventory.modules.ModuleBase; +import zmaster587.libVulpes.inventory.modules.ModuleText; + +/** + * Per-viewer status module for the Satellite Control Center. + * Forces a sync every 0.5s (9 ticks) while the GUI is open. + * Sends 4 ints: 0=status, 1=ppt, 2=data, 3=maxdata. + */ +public class ModuleSatelliteTerminal extends ModuleBase { + + private final ModuleText text; + private final int color; + private final IInventory inv; // client: read chip name + private final TileSatelliteTerminal tile; // server: compute values + + private static final long PERIOD_TICKS = 9L; + // {status, ppt, data, max} + private final int[] vals = new int[4]; + + + // Force burst every 9 ticks + private long lastPushBucket = Long.MIN_VALUE; + private boolean burstPending = false; + + + // Add field to track current chip/satellite identity (server-side) + private long lastSatId = Long.MIN_VALUE; // or int; use -1 for "no sat" + + private static long getCurrentSatId(TileSatelliteTerminal t) { + zmaster587.advancedRocketry.api.satellite.SatelliteBase sat = t.getSatelliteFromSlot(0); + if (sat == null) return -1L; + return sat.getId(); // adjust if getId() is int; cast/convert as needed + } + + public ModuleSatelliteTerminal(int x, int y, int color) { + this(x, y, color, null, null); + } + + public ModuleSatelliteTerminal(int x, int y, int color, IInventory inv, TileSatelliteTerminal tile) { + super(x, y); + this.color = color; + this.inv = inv; + this.tile = tile; + this.text = new ModuleText(x, y, "", color); + this.text.setText(LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.nolink")); + } + + @Override + public void renderForeground(int x, int y, int mouseX, int mouseY, float zLevel, + GuiContainer gui, FontRenderer font) { + } + + @Override + public void renderBackground(GuiContainer gui, int x, int y, int mouseX, int mouseY, + FontRenderer font) { + + text.renderBackground(gui, x, y, mouseX, mouseY, font); + } + + @Override + public List getSlots(Container container) { return Collections.emptyList(); } + + @Override public int numberOfChangesToSend() { return 4; } + + // Some libVulpes builds use needsUpdate; keep it mapped to our logic. + @Override + public boolean needsUpdate(int localId) { return isUpdateRequired(localId); } + + @Override + public void sendInitialChanges(Container container, IContainerListener listener, int moduleIndex) { + if (tile != null && !tile.getWorld().isRemote) { + int[] now = computeStatusFromTile(tile); + for (int i = 0; i < 4; i++) vals[i] = now[i]; + lastSatId = getCurrentSatId(tile); + long t = tile.getWorld().getTotalWorldTime(); + lastPushBucket = t / PERIOD_TICKS; + } + + for (int i = 0; i < 4; i++) { + listener.sendWindowProperty(container, moduleIndex + i, vals[i]); + } + burstPending = false; // reset + } + + @Override + public boolean isUpdateRequired(int relativeIdx) { + if (tile != null && !tile.getWorld().isRemote) { + final long t = tile.getWorld().getTotalWorldTime(); + final long bucket = t / PERIOD_TICKS; + + // Detect satellite/chip change + final long curSatId = getCurrentSatId(tile); + final boolean satChanged = (curSatId != lastSatId); + if (satChanged) lastSatId = curSatId; + + // Arm a new burst on bucket edge OR sat change + if (bucket != lastPushBucket || satChanged) { + lastPushBucket = bucket; + + // Compute all four, assign immediately so all lanes read the same snapshot + final int[] now = computeStatusFromTile(tile); + System.arraycopy(now, 0, vals, 0, 4); + + // One atomic send of all lanes this tick + burstPending = true; + + } + } + + // During a burst, ALL lanes return true so container sends 0..3 in one pass. + return burstPending; + } + + @Override + public void sendChanges(Container container, IContainerListener listener, + int variableId, int relativeIdx) { + // 'variableId' IS the global property id. Do NOT add relativeIdx. + listener.sendWindowProperty(container, variableId, vals[relativeIdx]); + + // Clear the burst only after the last lane goes out + if (relativeIdx == 3) { + burstPending = false; + } + } + + + + @Override + public void onChangeRecieved(int relativeIdx, int value) { + vals[relativeIdx] = value; + rebuildClientText(); + } + + // ---- Helpers ---- + + private static int[] computeStatusFromTile(TileSatelliteTerminal t) { + int status = 0, ppt = 0, data = 0, max = 0; + + SatelliteBase sat = t.getSatelliteFromSlot(0); + + // --- Case 1: No chip or invalid satellite --- + if (!(sat instanceof SatelliteData)) { + return new int[] { 0, 0, 0, 0 }; + } + + // --- Case 2: Valid satellite --- + boolean hasPower = t.getUniversalEnergyStored() >= t.getPowerPerOperation(); + int hereDim = DimensionManager.getEffectiveDimId(t.getWorld(), t.getPos()).getId(); + boolean inRange = PlanetaryTravelHelper.isTravelAnywhereInPlanetarySystem(sat.getDimensionId(), hereDim); + + if (!hasPower) { + status = 1; // Not enough power + } else if (!inRange) { + status = 2; // Out of range + } else { + status = 3; // OK and connected + + SatelliteData s = (SatelliteData) sat; + ppt = s.getPowerPerTick(); // Power generation rate + data = s.data.getData(); // Current data amount + max = s.data.getMaxData(); // Maximum storage + } + + // --- Always return all four fields --- + return new int[] { status, ppt, data, max }; + } + + + // Client: rebuild visible text; sat name read locally from chip + private void rebuildClientText() { + final int status = vals[0]; + final int ppt = vals[1]; + final int data = vals[2]; + final int max = vals[3]; + + String satName = null; + if (inv != null && inv.getSizeInventory() > 0) { + ItemStack stack0 = inv.getStackInSlot(0); + if (!stack0.isEmpty() && stack0.getItem() instanceof ItemSatelliteIdentificationChip) { + SatelliteBase sat = ItemSatelliteIdentificationChip.getSatellite(stack0); + if (sat != null) satName = sat.getName(); + } + } + + String msg; + if (status == 0) { + msg = LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.nolink"); + } else if (status == 1) { + msg = LibVulpes.proxy.getLocalizedString("msg.notenoughpower"); + } else if (status == 2) { + msg = LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.toofar"); + } else { + String info = LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.info"); + String power = LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.power"); + String dataLbl = LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.data"); + + msg = info + + "\n" + power + " " + ppt + + "\n" + dataLbl + " " + data + "/" + max; + } + + if (satName != null && !satName.isEmpty()) { + msg = satName + "\n\n" + msg; + } + + text.setText(msg); + text.setColor(color); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleSideSelectorTooltipOverlay.java b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleSideSelectorTooltipOverlay.java new file mode 100644 index 000000000..b7c1c1eb8 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleSideSelectorTooltipOverlay.java @@ -0,0 +1,83 @@ +package zmaster587.advancedRocketry.inventory.modules; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.resources.I18n; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.libVulpes.inventory.modules.ModuleBase; +import zmaster587.libVulpes.inventory.modules.ModuleBlockSideSelector; + +import java.util.ArrayList; +import java.util.List; + +@SideOnly(Side.CLIENT) +public class ModuleSideSelectorTooltipOverlay extends ModuleBase { + + private final ModuleBlockSideSelector selector; + private final String[] stateNames; + + // Match lib layout + side order + private final int[][] rects; + + // Keep allocations low: reuse list + private final List tooltip = new ArrayList<>(2); + + private static String dirName(int side) { + switch (side) { + case 0: return I18n.format("advancedrocketry.sideselector.direction.bottom"); + case 1: return I18n.format("advancedrocketry.sideselector.direction.top"); + case 2: return I18n.format("advancedrocketry.sideselector.direction.north"); + case 3: return I18n.format("advancedrocketry.sideselector.direction.south"); + case 4: return I18n.format("advancedrocketry.sideselector.direction.west"); + case 5: return I18n.format("advancedrocketry.sideselector.direction.east"); + default: return "?"; + } + } + + public ModuleSideSelectorTooltipOverlay(int offsetX, int offsetY, + ModuleBlockSideSelector selector, + String[] stateNames) { + super(offsetX, offsetY); + this.selector = selector; + this.stateNames = stateNames; + + // These positions match ModuleBlockSideSelector constructor + rects = new int[][]{ + {offsetX + 42, offsetY + 42, 16, 16}, // 0 bottom + {offsetX + 21, offsetY + 21, 16, 16}, // 1 top + {offsetX + 21, offsetY + 0, 16, 16}, // 2 north + {offsetX + 21, offsetY + 42, 16, 16}, // 3 south + {offsetX + 0, offsetY + 21, 16, 16}, // 4 west + {offsetX + 42, offsetY + 21, 16, 16} // 5 east + }; + } + + @Override + public void renderToolTip(int guiOffsetX, int guiOffsetY, + int mouseX, int mouseY, float zLevel, + GuiContainer gui, FontRenderer font) { + + for (int side = 0; side < 6; side++) { + int[] r = rects[side]; + + int rx = r[0]; + int ry = r[1]; + int rw = r[2]; + int rh = r[3]; + + if (mouseX >= rx && mouseX < rx + rw && mouseY >= ry && mouseY < ry + rh) { + int state = selector.getStateForSide(side); + String mode = (state >= 0 && state < stateNames.length) + ? stateNames[state] + : "Unknown"; + + tooltip.clear(); + tooltip.add(dirName(side) + ": " + mode); + + this.drawTooltip(gui, tooltip, mouseX, mouseY, zLevel, font); + return; + } + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleWirelessBufferBar.java b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleWirelessBufferBar.java new file mode 100644 index 000000000..3ec7c6e66 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/inventory/modules/ModuleWirelessBufferBar.java @@ -0,0 +1,138 @@ +package zmaster587.advancedRocketry.inventory.modules; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.resources.I18n; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.api.DataStorage; +import zmaster587.advancedRocketry.api.DataStorage.DataType; +import zmaster587.libVulpes.gui.CommonResources; +import zmaster587.libVulpes.inventory.modules.ModuleBase; + +import java.util.LinkedList; +import java.util.List; + +/** + * Minimal, read-only data bar for the wireless transceiver's internal buffer. + * - No buttons + * - No slots + * - Server-authoritative: syncs amount, max, and type via tiny window properties. + */ +public class ModuleWirelessBufferBar extends ModuleBase { + + // Reuse same visuals as ModuleData so it looks consistent + static final int BAR_Y_SIZE = 38; + static final int BAR_X_SIZE = 6; + static final int TEX_OFFSET_X = 0; + static final int TEX_OFFSET_Y = 215; + + private final DataStorage data; // points to your uiBuffer + private int prevAmount = -1; + private int prevMax = -1; + private int prevTypeOrdinal = -1; + + public ModuleWirelessBufferBar(int offsetX, int offsetY, DataStorage data) { + super(offsetX, offsetY); + this.data = data; + this.sizeX = 10; // hitbox-ish; not used for layout + this.sizeY = BAR_Y_SIZE + 12; + } + + @Override + public int numberOfChangesToSend() { + // amount, max, type + return 3; + } + + @Override + public boolean needsUpdate(int localId) { + switch (localId) { + case 0: return data.getData() != prevAmount; + case 1: return data.getMaxData() != prevMax; + case 2: return data.getDataType().ordinal() != prevTypeOrdinal; + default: return false; + } + } + + @Override + protected void updatePreviousState(int localId) { + if (localId == 0) prevAmount = data.getData(); + else if (localId == 1) prevMax = data.getMaxData(); + else if (localId == 2) prevTypeOrdinal = data.getDataType().ordinal(); + } + + @Override + public void sendChanges(net.minecraft.inventory.Container container, + net.minecraft.inventory.IContainerListener crafter, + int variableId, int localId) { + int v; + if (localId == 0) v = data.getData(); + else if (localId == 1) v = data.getMaxData(); + else /* localId == 2 */ v = data.getDataType().ordinal(); + crafter.sendWindowProperty(container, variableId, v); + } + + @Override + public void onChangeRecieved(int slot, int value) { + if (slot == 0) { + // amount (type set below or left unchanged) + data.setData(value, DataType.UNDEFINED); + } else if (slot == 1) { + data.setMaxData(value); + } else if (slot == 2) { + DataType t = DataType.values()[Math.max(0, Math.min(DataType.values().length - 1, value))]; + data.setDataType(t); + } + } + + @SideOnly(Side.CLIENT) + @Override + public void renderForeground(int guiOffsetX, int guiOffsetY, int mouseX, int mouseY, float zLevel, + GuiContainer gui, FontRenderer font) { + int relX = mouseX - offsetX; + int relY = mouseY - offsetY; + if (relX >= 0 && relX < BAR_X_SIZE && relY >= 0 && relY < BAR_Y_SIZE) { + List tt = new LinkedList<>(); + // "Data" + tt.add(net.minecraft.client.resources.I18n.format( + "msg.tooltip.data") + " " + data.getData() + " / " + data.getMaxData()); + + // "Type: %s" with translated type + String typeName = net.minecraft.client.resources.I18n.format(data.getDataType().toString()); + tt.add(net.minecraft.client.resources.I18n.format("msg.wirelessTransciever.type", typeName)); + + + this.drawTooltip(gui, tt, mouseX, mouseY, zLevel, font); + } + } + + + @SideOnly(Side.CLIENT) + @Override + public void renderBackground(GuiContainer gui, int x, int y, int mouseX, int mouseY, FontRenderer font) { + // Bind the correct texture (same sheet as ModuleData) + gui.mc.getTextureManager().bindTexture(CommonResources.genericBackground); + + // Draw only the bar frame (8x40 at UV 176,18) + gui.drawTexturedModalRect(offsetX + x, offsetY + y, 176, 18, 8, 40); + + // Compute fill amount + int max = Math.max(1, data.getMaxData()); + float percent = Math.min(1f, Math.max(0f, data.getData() / (float) max)); + int filled = (int) (percent * BAR_Y_SIZE); + + // Draw the green fill (6 x filled) from UV (0, 215 + (BAR_Y_SIZE - filled)) + // Fill grows upward inside the frame + if (filled > 0) { + gui.drawTexturedModalRect( + offsetX + x + 1, + offsetY + y + 1 + (BAR_Y_SIZE - filled), + 0, + 215 + (BAR_Y_SIZE - filled), + BAR_X_SIZE, + filled + ); + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/inventory/modules/SlotData.java b/src/main/java/zmaster587/advancedRocketry/inventory/modules/SlotData.java index db218d096..59bf320de 100644 --- a/src/main/java/zmaster587/advancedRocketry/inventory/modules/SlotData.java +++ b/src/main/java/zmaster587/advancedRocketry/inventory/modules/SlotData.java @@ -3,7 +3,7 @@ import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; -import zmaster587.advancedRocketry.item.ItemData; +import zmaster587.advancedRocketry.item.IDataItem; import javax.annotation.Nonnull; @@ -17,9 +17,16 @@ public SlotData(IInventory p_i1824_1_, int p_i1824_2_, int p_i1824_3_, @Override public boolean isItemValid(@Nonnull ItemStack stack) { - if (stack.isEmpty() || stack.getItem() instanceof ItemData) - return super.isItemValid(stack); - return false; + return !stack.isEmpty() && stack.getItem() instanceof IDataItem; } + @Override + public int getSlotStackLimit() { + return 1; + } + + @Override + public int getItemStackLimit(@Nonnull ItemStack stack) { + return 1; + } } diff --git a/src/main/java/zmaster587/advancedRocketry/item/IDataItem.java b/src/main/java/zmaster587/advancedRocketry/item/IDataItem.java new file mode 100644 index 000000000..9d6d66b60 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/item/IDataItem.java @@ -0,0 +1,70 @@ +package zmaster587.advancedRocketry.item; + +import net.minecraft.item.ItemStack; +import zmaster587.advancedRocketry.api.DataStorage; + +import javax.annotation.Nonnull; + +/** + * Common interface for any item that behaves like an ItemData-style data container. + * + * Goal: + * - Allow commands (/advancedrocketry filldata), GUI slots (SlotData), + * and machine logic (TileDataBus/Observatory/Terminal/etc) + * to accept multiple item implementations without hard-typing to ItemData. + * + * Contract: + * - Implementations should store their DataStorage in the root tag of the ItemStack + * in the same shape as DataStorage#writeToNBT / readFromNBT expects. + * - getDataStorage MUST return a DataStorage instance representing the stack state. + * - setData/addData/removeData MUST persist changes back into the stack NBT. + */ +public interface IDataItem { + + /** + * @return max capacity for this specific stack. + * Implementations may compute this from damage, NBT, config, etc. + */ + int getMaxData(@Nonnull ItemStack stack); + + /** + * Reads a DataStorage snapshot from the stack. + * Implementations should ensure returned storage has correct maxData applied. + */ + @Nonnull + DataStorage getDataStorage(@Nonnull ItemStack stack); + + /** + * Convenience read of current amount. + */ + default int getData(@Nonnull ItemStack stack) { + return getDataStorage(stack).getData(); + } + + /** + * Convenience read of current type. + */ + @Nonnull + default DataStorage.DataType getDataType(@Nonnull ItemStack stack) { + return getDataStorage(stack).getDataType(); + } + + /** + * Adds data of the given type to this stack. + * + * @return amount actually added + */ + int addData(@Nonnull ItemStack stack, int amount, @Nonnull DataStorage.DataType dataType); + + /** + * Removes data of the given type from this stack. + * + * @return amount actually removed + */ + int removeData(@Nonnull ItemStack stack, int amount, @Nonnull DataStorage.DataType dataType); + + /** + * Sets (overwrites) data amount + type on this stack. + */ + void setData(@Nonnull ItemStack stack, int amount, @Nonnull DataStorage.DataType dataType); +} diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemAsteroidChip.java b/src/main/java/zmaster587/advancedRocketry/item/ItemAsteroidChip.java index 4ecc42e2f..2c2ee55ac 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemAsteroidChip.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemAsteroidChip.java @@ -1,9 +1,13 @@ package zmaster587.advancedRocketry.item; import com.mojang.realmsclient.gui.ChatFormatting; + +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import zmaster587.libVulpes.LibVulpes; @@ -23,7 +27,14 @@ public boolean isDamageable() { return false; } - + public static String shortDisplayId(Long uuid, String type) { + long base = (uuid == null) ? 0L : uuid; + long th = (type == null) ? 0L : Integer.toUnsignedLong(type.hashCode()); + long disp = mix64(base ^ (th << 1)); + String hex = Long.toUnsignedString(disp, 16).toUpperCase(); + int N = 6; + return (hex.length() > N) ? hex.substring(hex.length() - N) : hex; + } /** * Removes any Information and reset the stack to a default state * @@ -66,25 +77,50 @@ public void setType(@Nonnull ItemStack stack, String type) { nbt.setString(astType, type); stack.setTagCompound(nbt); } + // SplitMix64 mixer: great diffusion, tiny cost + // Make Unique ID from UUID and type (looks random, but is deterministic) + // Only for tooltip display purposes. Actual NBT untouched. + private static long mix64(long z) { + z += 0x9E3779B97F4A7C15L; + z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L; + z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL; + return z ^ (z >>> 31); + } - @Override - public void addInformation(@Nonnull ItemStack stack, World player, List list, ITooltipFlag bool) { + // Deterministic display id from UUID and type (no world dependence) + private static long makeDisplayId(Long uuid, String type) { + long base = (uuid == null) ? 0L : uuid; + long th = (type == null) ? 0L : Integer.toUnsignedLong(type.hashCode()); + return mix64(base ^ (th << 1)); // fold in type so same UUID/different types look different + } + @Override + public void addInformation(@Nonnull ItemStack stack, World world, List list, ITooltipFlag flag) { if (!stack.hasTagCompound()) { list.add(LibVulpes.proxy.getLocalizedString("msg.unprogrammed")); - } else { - if (stack.getItemDamage() == 0) { + return; + } + if (stack.getItemDamage() == 0) { + Long id = getUUID(stack); + String type = getType(stack); - list.add(LibVulpes.proxy.getLocalizedString("msg.asteroidChip.asteroid") + "-" + ChatFormatting.DARK_GREEN + getUUID(stack)); + if (type != null && !type.isEmpty()) { + list.add(LibVulpes.proxy.getLocalizedString("msg.asteroidChip.type") + ": " + + ChatFormatting.AQUA + type); + } - super.addInformation(stack, player, list, bool); + // Tooltip-only, random-looking but deterministic + final long disp = makeDisplayId(id, type); + final String hex = Long.toUnsignedString(disp, 16).toUpperCase(); - //list.add("Mass: " + unknown); - //list.add("Atmosphere Density: " + unknown); - //list.add("Distance From Star: " + unknown); + // Fixed-length visual tag + final int N = 6; + final String shortHex = (hex.length() > N) ? hex.substring(hex.length() - N) : hex; - } + list.add(LibVulpes.proxy.getLocalizedString("msg.asteroidChip.asteroid") + ": " + + ChatFormatting.DARK_GREEN + shortHex); + + super.addInformation(stack, world, list, flag); } } - } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemAtmosphereAnalzer.java b/src/main/java/zmaster587/advancedRocketry/item/ItemAtmosphereAnalzer.java index e02ffccf4..06f01c537 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemAtmosphereAnalzer.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemAtmosphereAnalzer.java @@ -6,6 +6,7 @@ import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.EntityEquipmentSlot; @@ -26,6 +27,7 @@ import org.lwjgl.opengl.GL11; import zmaster587.advancedRocketry.atmosphere.AtmosphereHandler; import zmaster587.advancedRocketry.atmosphere.AtmosphereType; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.advancedRocketry.event.RocketEventHandler; import zmaster587.advancedRocketry.inventory.TextureResources; @@ -107,6 +109,14 @@ public boolean isAllowedInSlot(@Nonnull ItemStack componentStack, EntityEquipmen return targetSlot == EntityEquipmentSlot.HEAD; } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.atmanalyzer", insertAt); + } + + @Override @SideOnly(Side.CLIENT) public void renderScreen(@Nonnull ItemStack componentStack, List modules, @@ -149,5 +159,4 @@ public void renderScreen(@Nonnull ItemStack componentStack, List modu public ResourceIcon getComponentIcon(@Nonnull ItemStack armorStack) { return null; } - } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemBeaconFinder.java b/src/main/java/zmaster587/advancedRocketry/item/ItemBeaconFinder.java index 3c385c1ad..27e263ec9 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemBeaconFinder.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemBeaconFinder.java @@ -6,6 +6,7 @@ import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.EntityEquipmentSlot; @@ -19,6 +20,8 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.lwjgl.opengl.GL11; + +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.libVulpes.api.IArmorComponent; import zmaster587.libVulpes.client.ResourceIcon; @@ -27,6 +30,8 @@ import zmaster587.libVulpes.util.HashedBlockPosition; import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import java.util.List; public class ItemBeaconFinder extends Item implements IArmorComponent { @@ -95,6 +100,13 @@ public void renderScreen(@Nonnull ItemStack componentStack, List modu } } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.beaconfinder", insertAt); + } + @Override public ResourceIcon getComponentIcon(@Nonnull ItemStack armorStack) { return null; diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemBiomeChanger.java b/src/main/java/zmaster587/advancedRocketry/item/ItemBiomeChanger.java index eb460113d..220c408d2 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemBiomeChanger.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemBiomeChanger.java @@ -64,27 +64,29 @@ public List getModules(int id, EntityPlayer player) { } @Override - public void addInformation(@Nonnull ItemStack stack, World player, List list, ITooltipFlag arg5) { + public void addInformation(@Nonnull ItemStack stack, World world, List list, ITooltipFlag flag) { SatelliteBase sat = SatelliteRegistry.getSatellite(stack); + SatelliteBiomeChanger mapping = sat instanceof SatelliteBiomeChanger ? (SatelliteBiomeChanger) sat : null; - SatelliteBiomeChanger mapping = null; - if (sat instanceof SatelliteBiomeChanger) - mapping = (SatelliteBiomeChanger) sat; + // If unprogrammed, let the superclass handle the "unprogrammed" tooltip so it only shows once + if (!stack.hasTagCompound()) { + super.addInformation(stack, world, list, flag); + return; + } - if (!stack.hasTagCompound()) - list.add(LibVulpes.proxy.getLocalizedString("msg.unprogrammed")); - else if (mapping == null) + if (mapping == null) { list.add(LibVulpes.proxy.getLocalizedString("msg.biomechanger.nosat")); - else if (mapping.getDimensionId() == player.provider.getDimension()) { + } else if (mapping.getDimensionId() == world.provider.getDimension()) { list.add(LibVulpes.proxy.getLocalizedString("msg.connected")); - if (mapping.getBiome()!=null) + if (mapping.getBiome() != null) list.add(LibVulpes.proxy.getLocalizedString("msg.biomechanger.selBiome") + mapping.getBiome().getBiomeName()); list.add(LibVulpes.proxy.getLocalizedString("msg.biomechanger.numBiome") + mapping.discoveredBiomes().size()); - } else + } else { list.add(LibVulpes.proxy.getLocalizedString("msg.notconnected")); + } - super.addInformation(stack, player, list, arg5); + super.addInformation(stack, world, list, flag); } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemBlockDataBusBig.java b/src/main/java/zmaster587/advancedRocketry/item/ItemBlockDataBusBig.java new file mode 100644 index 000000000..9e8f465fa --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemBlockDataBusBig.java @@ -0,0 +1,176 @@ +package zmaster587.advancedRocketry.item; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.api.ARConfiguration; +import zmaster587.advancedRocketry.api.DataStorage; +import zmaster587.advancedRocketry.tile.hatch.TileDataBusBig; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class ItemBlockDataBusBig extends ItemBlock implements IDataItem { + + // Keep this local and explicit + private static final int BASE_MAX_DATA = 2000; // match TileDataBus base + private static final int DEFAULT_MULT = 4; + + public ItemBlockDataBusBig(Block block) { + super(block); + setHasSubtypes(false); + setMaxDamage(0); + } + + // ---- IDataItem ---- + + private static int getConfiguredMultSafe() { + int mult = DEFAULT_MULT; + + try { + ARConfiguration cfg = ARConfiguration.getCurrentConfig(); + if (cfg != null) mult = cfg.dataBusBigMultiplier; + } catch (Throwable ignored) {} + + if (mult < 1) mult = 1; + else if (mult > 20) mult = 20; + + return mult; + } + + private static int computeMaxData() { + int mult = getConfiguredMultSafe(); + long maxLong = (long) BASE_MAX_DATA * (long) mult; + return maxLong > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxLong; + } + + @Override + public int getMaxData(@Nonnull ItemStack stack) { + return computeMaxData(); + } + + + + @Override + @Nonnull + public DataStorage getDataStorage(@Nonnull ItemStack stack) { + DataStorage data = new DataStorage(); + + if (!stack.hasTagCompound()) { + data.setMaxData(getMaxData(stack)); + data.setData(0, DataStorage.DataType.UNDEFINED); + } else { + data.readFromNBT(stack.getTagCompound()); + data.setMaxData(getMaxData(stack)); + if (data.getData() > data.getMaxData()) { + data.setData(data.getMaxData(), data.getDataType()); + } + } + return data; + } + + @Override + public int addData(@Nonnull ItemStack stack, int amount, @Nonnull DataStorage.DataType dataType) { + DataStorage data = getDataStorage(stack); + + int added = data.addData(amount, dataType, true); + + NBTTagCompound nbt = new NBTTagCompound(); + data.writeToNBT(nbt); + stack.setTagCompound(nbt); + + return added; + } + + @Override + public int removeData(@Nonnull ItemStack stack, int amount, @Nonnull DataStorage.DataType dataType) { + DataStorage data = getDataStorage(stack); + + int removed = data.removeData(amount, true); + + NBTTagCompound nbt = new NBTTagCompound(); + data.writeToNBT(nbt); + stack.setTagCompound(nbt); + + return removed; + } + + @Override + public void setData(@Nonnull ItemStack stack, int amount, @Nonnull DataStorage.DataType dataType) { + DataStorage data = getDataStorage(stack); + + data.setData(amount, dataType); + + NBTTagCompound nbt = new NBTTagCompound(); + data.writeToNBT(nbt); + stack.setTagCompound(nbt); + } + + // ---- stacking rule (same behavior as ItemData) ---- + @Override + public int getItemStackLimit(@Nonnull ItemStack stack) { + return getData(stack) == 0 ? super.getItemStackLimit(stack) : 1; + } + + // ---- place NBT into TE ---- + @Override + public boolean placeBlockAt(ItemStack stack, EntityPlayer player, World world, BlockPos pos, + EnumFacing side, float hitX, float hitY, float hitZ, IBlockState newState) { + + boolean placed = super.placeBlockAt(stack, player, world, pos, side, hitX, hitY, hitZ, newState); + if (!placed || world.isRemote) return placed; + + if (!stack.hasTagCompound()) return placed; + + TileEntity te = world.getTileEntity(pos); + if (!(te instanceof TileDataBusBig)) return placed; + + TileDataBusBig bus = (TileDataBusBig) te; + + NBTTagCompound tag = stack.getTagCompound(); + if (tag != null) { + NBTTagCompound teTag = bus.writeToNBT(new NBTTagCompound()); + teTag.merge(tag); + bus.readFromNBT(teTag); + bus.markDirty(); + world.notifyBlockUpdate(pos, newState, newState, 3); + } + + return placed; + } + + // ---- tooltip parity with ItemData ---- + @Override + @SideOnly(Side.CLIENT) + public void addInformation(@Nonnull ItemStack stack, @Nullable World world, + List list, ITooltipFlag flag) { + + DataStorage data = getDataStorage(stack); + + // Header + list.add(I18n.format("tooltip.advancedrocketry.databusbig.header")); + + // Type + String typeText = I18n.format(data.getDataType().toString()); + list.add(I18n.format("tooltip.advancedrocketry.itemdata.type") + typeText); + + // Data + list.add(I18n.format("tooltip.advancedrocketry.itemdata.data") + + TextFormatting.GOLD + data.getData() + + TextFormatting.WHITE + " / " + + TextFormatting.GOLD + data.getMaxData()); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemBlockFluidTank.java b/src/main/java/zmaster587/advancedRocketry/item/ItemBlockFluidTank.java index 614a958ae..c3941398c 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemBlockFluidTank.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemBlockFluidTank.java @@ -2,6 +2,8 @@ import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemBlock; @@ -10,11 +12,15 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidTank; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import org.lwjgl.input.Keyboard; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.tile.TileFluidTank; @@ -29,48 +35,73 @@ public ItemBlockFluidTank(Block block) { super(block); } + /** Capacity of the item tank in mB, using the same base as the block (64_000 mB), + * preserving fractional multipliers and clamping to int range. */ + private static int getCapMb() { + // Math.round(double) -> long; keep it in long, then clamp to int range + long computed = Math.round(64000d * ARConfiguration.getCurrentConfig().blockTankCapacity); + return (int) Math.min(Integer.MAX_VALUE, Math.max(0L, computed)); + } + @Override + @SideOnly(Side.CLIENT) @ParametersAreNonnullByDefault - public void addInformation(@Nonnull ItemStack stack, @Nullable World world, List list, ITooltipFlag bool) { - super.addInformation(stack, world, list, bool); + public void addInformation(@Nonnull ItemStack stack, @Nullable World world, List list, ITooltipFlag flag) { + super.addInformation(stack, world, list, flag); - FluidStack fluidStack = getFluid(stack); + final int capMb = getCapMb(); + final FluidStack fs = getFluid(stack); - if (fluidStack == null) { - list.add("Empty"); - } else { - list.add(fluidStack.getLocalizedName() + ": " + fluidStack.amount/1000 + "/"+64* ARConfiguration.getCurrentConfig().blockTankCapacity+"b"); - } + final String fluidName = (fs != null && fs.getFluid() != null) ? fs.getLocalizedName() : I18n.format("tooltip.advancedrocketry.fluidtank.empty");; + final int amount = (fs != null) ? fs.amount : 0; + + list.add(I18n.format("tooltip.advancedrocketry.fluidtank.fluid") + fluidName); + list.add(I18n.format("tooltip.advancedrocketry.fluidtank.level") + amount + "/" + capMb + " mB"); + + + // --- SHIFT for more info --- + if (GuiScreen.isShiftKeyDown()) { + list.add(TextFormatting.GRAY + I18n.format("tooltip.advancedrocketry.fluidtank.shift.1")); + } else if (I18n.hasKey("tooltip.advancedrocketry.hold_shift")) { + list.add(TextFormatting.DARK_GRAY.toString() + TextFormatting.ITALIC + + I18n.format("tooltip.advancedrocketry.hold_shift")); + } } @Override @ParametersAreNonnullByDefault - public boolean placeBlockAt(@Nonnull ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ, IBlockState newState) { - super.placeBlockAt(stack, player, world, pos, side, hitX, hitY, hitZ, newState); - + public boolean placeBlockAt(@Nonnull ItemStack stack, EntityPlayer player, World world, BlockPos pos, + EnumFacing side, float hitX, float hitY, float hitZ, IBlockState newState) { + if (!super.placeBlockAt(stack, player, world, pos, side, hitX, hitY, hitZ, newState)) { + return false; + } TileEntity tile = world.getTileEntity(pos); - if (tile instanceof TileFluidTank) { IFluidHandler handler = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, EnumFacing.DOWN); - ItemStack stack2 = stack.copy(); - stack2.setCount(1); - handler.fill(drain(stack2, Integer.MAX_VALUE), true); + if (handler != null) { + ItemStack one = stack.copy(); + one.setCount(1); + FluidStack drained = drain(one, Integer.MAX_VALUE); + if (drained != null && drained.amount > 0) { + handler.fill(drained, true); + } + } } - return true; } public void fill(@Nonnull ItemStack stack, FluidStack fluid) { - NBTTagCompound nbt; - FluidTank tank = new FluidTank((int) (640000* ARConfiguration.getCurrentConfig().blockTankCapacity)); + FluidTank tank = new FluidTank(getCapMb()); if (stack.hasTagCompound()) { nbt = stack.getTagCompound(); tank.readFromNBT(nbt); - } else + } else { nbt = new NBTTagCompound(); + } - tank.fill(fluid, true); + if (fluid != null) {tank.fill(fluid, true); + } tank.writeToNBT(nbt); stack.setTagCompound(nbt); @@ -78,29 +109,29 @@ public void fill(@Nonnull ItemStack stack, FluidStack fluid) { public FluidStack drain(@Nonnull ItemStack stack, int amt) { NBTTagCompound nbt; - FluidTank tank = new FluidTank((int) (640000* ARConfiguration.getCurrentConfig().blockTankCapacity)); + FluidTank tank = new FluidTank(getCapMb()); if (stack.hasTagCompound()) { nbt = stack.getTagCompound(); tank.readFromNBT(nbt); - } else + } else { nbt = new NBTTagCompound(); + } - FluidStack stack2 = tank.drain(amt, true); + FluidStack drained = tank.drain(amt, true); tank.writeToNBT(nbt); stack.setTagCompound(nbt); - return stack2; + return drained; } public FluidStack getFluid(@Nonnull ItemStack stack) { NBTTagCompound nbt; - FluidTank tank = new FluidTank((int) (640000* ARConfiguration.getCurrentConfig().blockTankCapacity)); + FluidTank tank = new FluidTank(getCapMb()); if (stack.hasTagCompound()) { nbt = stack.getTagCompound(); tank.readFromNBT(nbt); } - return tank.getFluid(); } } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemData.java b/src/main/java/zmaster587/advancedRocketry/item/ItemData.java index 61ee7848f..010ea3d4e 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemData.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemData.java @@ -4,6 +4,7 @@ import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -11,21 +12,27 @@ import zmaster587.libVulpes.items.ItemIngredient; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.List; -public class ItemData extends ItemIngredient { - - int maxData; +public class ItemData extends ItemIngredient implements IDataItem { public ItemData() { super(1); setMaxStackSize(1); } + // ---- OLD API (keep) ---- public int getMaxData(int damage) { return damage == 0 ? 1000 : 0; } + // ---- NEW API (IDataItem) ---- + @Override + public int getMaxData(@Nonnull ItemStack stack) { + return getMaxData(stack.getItemDamage()); + } + @Override public int getItemStackLimit(@Nonnull ItemStack stack) { return getData(stack) == 0 ? super.getItemStackLimit(stack) : 1; @@ -39,21 +46,29 @@ public DataStorage.DataType getDataType(@Nonnull ItemStack stack) { return getDataStorage(stack).getDataType(); } + @Override + @Nonnull public DataStorage getDataStorage(@Nonnull ItemStack item) { DataStorage data = new DataStorage(); if (!item.hasTagCompound()) { - data.setMaxData(getMaxData(item.getItemDamage())); + data.setMaxData(getMaxData(item)); NBTTagCompound nbt = new NBTTagCompound(); data.writeToNBT(nbt); - } else + // NOTE: original ItemData does NOT auto-attach tag here. + // Keep behavior to avoid subtle side effects. + } else { data.readFromNBT(item.getTagCompound()); + // make sure capacity is correct for this item + data.setMaxData(getMaxData(item)); + } return data; } - public int addData(@Nonnull ItemStack item, int amount, DataStorage.DataType dataType) { + @Override + public int addData(@Nonnull ItemStack item, int amount, @Nonnull DataStorage.DataType dataType) { DataStorage data = getDataStorage(item); int amt = data.addData(amount, dataType, true); @@ -65,7 +80,8 @@ public int addData(@Nonnull ItemStack item, int amount, DataStorage.DataType dat return amt; } - public int removeData(@Nonnull ItemStack item, int amount, DataStorage.DataType dataType) { + @Override + public int removeData(@Nonnull ItemStack item, int amount, @Nonnull DataStorage.DataType dataType) { DataStorage data = getDataStorage(item); int amt = data.removeData(amount, true); @@ -77,7 +93,8 @@ public int removeData(@Nonnull ItemStack item, int amount, DataStorage.DataType return amt; } - public void setData(@Nonnull ItemStack item, int amount, DataStorage.DataType dataType) { + @Override + public void setData(@Nonnull ItemStack item, int amount, @Nonnull DataStorage.DataType dataType) { DataStorage data = getDataStorage(item); data.setData(amount, dataType); @@ -89,14 +106,31 @@ public void setData(@Nonnull ItemStack item, int amount, DataStorage.DataType da @Override @SideOnly(Side.CLIENT) - public void addInformation(@Nonnull ItemStack stack, World player, List list, ITooltipFlag bool) { - super.addInformation(stack, player, list, bool); + public void addInformation(@Nonnull ItemStack stack, @Nullable World world, + List list, ITooltipFlag flag) { + super.addInformation(stack, world, list, flag); DataStorage data = getDataStorage(stack); - list.add(data.getData() + " / " + data.getMaxData() + " Data"); - list.add(I18n.format(data.getDataType().toString())); - + // Type: + list.add(I18n.format("tooltip.advancedrocketry.itemdata.header")); + String typeText = I18n.format(data.getDataType().toString()); + list.add(I18n.format("tooltip.advancedrocketry.itemdata.type") + typeText); + + // Data: + list.add(I18n.format("tooltip.advancedrocketry.itemdata.data") + + TextFormatting.GOLD + data.getData() + + TextFormatting.WHITE + " / " + + TextFormatting.GOLD + data.getMaxData()); + + // Hold Shift: + if (net.minecraft.client.gui.GuiScreen.isShiftKeyDown()) { + list.add(TextFormatting.GRAY + + I18n.format("tooltip.advancedrocketry.itemdataunit.shift.1")); + } else if (I18n.hasKey("tooltip.advancedrocketry.hold_shift")) { + list.add(TextFormatting.DARK_GRAY.toString() + + TextFormatting.ITALIC + + I18n.format("tooltip.advancedrocketry.hold_shift")); + } } - } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemHovercraft.java b/src/main/java/zmaster587/advancedRocketry/item/ItemHovercraft.java index f6c2c96ae..aa8bfcacc 100755 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemHovercraft.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemHovercraft.java @@ -98,9 +98,4 @@ public ActionResult onItemRightClick(World worldIn, EntityPlayer play } } } - - @Override - public void addInformation(@Nonnull ItemStack stack, World worldIn, List tooltip, ITooltipFlag flagIn) { - tooltip.add(LibVulpes.proxy.getLocalizedString("item.hovercraft.tooltip")); - } } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemIdWithName.java b/src/main/java/zmaster587/advancedRocketry/item/ItemIdWithName.java index 308c366ff..1743f82b1 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemIdWithName.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemIdWithName.java @@ -15,12 +15,9 @@ public class ItemIdWithName extends Item { public void setName(@Nonnull ItemStack stack, String name) { - - if (stack.hasTagCompound()) { - NBTTagCompound nbt = stack.getTagCompound(); - nbt.setString("name", name); - stack.setTagCompound(nbt); - } + NBTTagCompound nbt = stack.hasTagCompound() ? stack.getTagCompound() : new NBTTagCompound(); + nbt.setString("name", name); + stack.setTagCompound(nbt); } public String getName(@Nonnull ItemStack stack) { @@ -38,8 +35,16 @@ public String getName(@Nonnull ItemStack stack) { public void addInformation(@Nonnull ItemStack stack, World player, List list, ITooltipFlag bool) { if (stack.getItemDamage() == -1) { list.add(ChatFormatting.GRAY + "Unprogrammed"); - } else { - list.add(getName(stack)); + return; } + + String keyOrName = getName(stack); + if (keyOrName == null || keyOrName.isEmpty()) { + return; + } + + // If it's a lang key, this becomes localized; if not, it returns the input unchanged. + String translated = net.minecraft.client.resources.I18n.format(keyOrName); + list.add(translated); } } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemJackHammer.java b/src/main/java/zmaster587/advancedRocketry/item/ItemJackHammer.java index 6aa3551b3..560f3554d 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemJackHammer.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemJackHammer.java @@ -4,14 +4,22 @@ import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemTool; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.oredict.OreDictionary; import zmaster587.advancedRocketry.api.MaterialGeode; +import zmaster587.advancedRocketry.client.TooltipInjector; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; + +import java.util.List; import java.util.Set; public class ItemJackHammer extends ItemTool { @@ -40,4 +48,12 @@ public float getDestroySpeed(@Nonnull ItemStack stack, IBlockState state) { public boolean canHarvestBlock(IBlockState blockIn) { return true; } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.jackhammer", insertAt); + } + } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemMultiData.java b/src/main/java/zmaster587/advancedRocketry/item/ItemMultiData.java index b0b72a2bf..2e2e4e145 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemMultiData.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemMultiData.java @@ -42,6 +42,14 @@ public int getData(@Nonnull ItemStack stack, DataStorage.DataType type) { public int getMaxData(@Nonnull ItemStack stack) { return getDataStorage(stack).getMaxData(); } + // Supported types for this item. Others will be ignored. + // FIX IF WE ADD MORE TYPES TO DataStorage.DataType + private static final java.util.EnumSet SUPPORTED_TYPES = + java.util.EnumSet.of( + DataStorage.DataType.COMPOSITION, + DataStorage.DataType.MASS, + DataStorage.DataType.DISTANCE + ); private MultiData getDataStorage(@Nonnull ItemStack item) { @@ -117,9 +125,9 @@ public void addInformation(@Nonnull ItemStack stack, World player, List MultiData data = getDataStorage(stack); - for (DataStorage.DataType type : DataStorage.DataType.values()) { - if (type != DataStorage.DataType.UNDEFINED) - list.add(data.getDataAmount(type) + " / " + data.getMaxData() + " " + I18n.format(type.toString(), new Object[0]) + " Data"); + for (DataStorage.DataType type : SUPPORTED_TYPES) { + final int amt = data.getDataAmount(type); + list.add(amt + " / " + data.getMaxData() + " " + I18n.format(type.toString()) + " " + I18n.format("data.label.data")); } } } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemPackedStructure.java b/src/main/java/zmaster587/advancedRocketry/item/ItemPackedStructure.java index 70e68cae1..3bb596dac 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemPackedStructure.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemPackedStructure.java @@ -1,11 +1,19 @@ package zmaster587.advancedRocketry.item; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.util.StorageChunk; +import java.util.List; + import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ItemPackedStructure extends Item { @@ -38,4 +46,11 @@ public StorageChunk getStructure(@Nonnull ItemStack stack) { } return null; } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.packedstructure", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemSatellite.java b/src/main/java/zmaster587/advancedRocketry/item/ItemSatellite.java index 8510fc3f4..0722498da 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemSatellite.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemSatellite.java @@ -8,17 +8,89 @@ import zmaster587.advancedRocketry.api.SatelliteRegistry; import zmaster587.advancedRocketry.api.satellite.SatelliteBase; import zmaster587.advancedRocketry.api.satellite.SatelliteProperties; +import zmaster587.advancedRocketry.satellite.SatelliteData; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.util.EmbeddedInventory; import zmaster587.libVulpes.util.ZUtils; import javax.annotation.Nonnull; import java.util.List; +import java.util.Locale; public class ItemSatellite extends ItemIdWithName { + private static final int CORE_SLOT = 0; + private static final int FIRST_MOD_SLOT = 1; + private static final int LAST_MOD_SLOT = 6; + + /** + * Math from SatelliteData: + * collectionTime = (int) (200 / Math.sqrt(0.1 * powerConsumption)); + * and fallback: + * if (collectionTime == 0) collectionTime = 200; + */ + private static int calcCollectionTimeTicks(int powerGeneration) { + if (powerGeneration <= 0) return 0; + int ct = (int) (200.0 / Math.sqrt(0.1 * (double) powerGeneration)); + return (ct == 0) ? 200 : ct; + } + + /** SatelliteData produces 1 data per collectionTime ticks; 20 ticks/sec. */ + private static double calcDataPerSecond(int powerGeneration) { + int ct = calcCollectionTimeTicks(powerGeneration); + if (ct <= 0) return 0.0; + return 20.0 / (double) ct; + } + + private static String makeDataGenLine(double dataPerSec) { + // Stable decimal separator regardless of OS locale + String val = String.format(Locale.ROOT, "%.3f", dataPerSec); + + // Preferred: vanilla I18n formatting (client-side tooltip) + String localized = net.minecraft.client.resources.I18n.format("msg.itemsatellite.datagen", val); + + // If lang key is missing, I18n returns the key itself; degrade gracefully + if ("msg.itemsatellite.datagen".equals(localized)) { + return "Data gen: " + val + "/s"; + } + return localized; + } + + //Guarding inventory to ensure only valid items are placed in slots. + public static class SatelliteModuleInventory extends EmbeddedInventory { + public SatelliteModuleInventory() { super(7); } // slots 0-6 embedded from chassis + + @Override + public boolean isItemValidForSlot(int slot, @Nonnull ItemStack stack) { + if (stack.isEmpty()) return false; + + SatelliteProperties p = SatelliteRegistry.getSatelliteProperty(stack); + if (p == null) return false; + int f = p.getPropertyFlag(); + // only allow appropriate items in appropriate slots + if (slot == CORE_SLOT) { + return SatelliteProperties.Property.MAIN.isOfType(f); + } + + if (slot >= FIRST_MOD_SLOT && slot <= LAST_MOD_SLOT) { + return SatelliteProperties.Property.POWER_GEN.isOfType(f) || + SatelliteProperties.Property.BATTERY.isOfType(f) || + SatelliteProperties.Property.DATA.isOfType(f); + } + return false; + } + + + @Override + public void setInventorySlotContents(int index, ItemStack stack) { + if (!stack.isEmpty() && !isItemValidForSlot(index, stack)) return; + super.setInventorySlotContents(index, stack); + } + } + + public EmbeddedInventory readInvFromNBT(@Nonnull ItemStack stackIn) { - EmbeddedInventory inv = new EmbeddedInventory(7); + EmbeddedInventory inv = new SatelliteModuleInventory(); // slots 0-6 embedded from chassis, guarded by class above if (!stackIn.hasTagCompound() || !stackIn.getTagCompound().hasKey("inv")) return inv; @@ -59,47 +131,146 @@ public void setSatellite(@Nonnull ItemStack stack, SatelliteProperties propertie } - @Override - public void addInformation(@Nonnull ItemStack stack, World player, List list, ITooltipFlag bool) { - if (stack.getItem() instanceof ItemSatellite && SatelliteRegistry.getSatelliteProperties(stack) != null) { - SatelliteProperties properties = SatelliteRegistry.getSatelliteProperties(stack); + public void addInformation(@Nonnull ItemStack stack, World world, List list, ITooltipFlag flag) { + // Assembled = has properties AND a real ID (>0) + SatelliteProperties props = SatelliteRegistry.getSatelliteProperties(stack); + final boolean isAssembled = (props != null && props.getId() > 0); + if (isAssembled) { int dataStorage, powerGeneration, powerStorage; float weight; - list.add(getName(stack)); - list.add("ID: " + properties.getId()); + String display = getName(stack); // fallback (may be key) + SatelliteBase base = SatelliteRegistry.getNewSatellite(props.getSatelliteType()); + if (base != null) display = base.getName(); + + // translate if it’s a key; if not, returns input unchanged + display = net.minecraft.client.resources.I18n.format(display); + + list.add(display); + list.add("ID: " + props.getId()); - if (SatelliteProperties.Property.BATTERY.isOfType(properties.getPropertyFlag())) { - if ((powerStorage = properties.getPowerStorage()) > 0) - list.add(LibVulpes.proxy.getLocalizedString("msg.itemsatellite.pwr") + powerStorage); - else - list.add(ChatFormatting.RED + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.nopwr")); + if (SatelliteProperties.Property.BATTERY.isOfType(props.getPropertyFlag())) { + powerStorage = props.getPowerStorage(); + list.add((powerStorage > 0) + ? LibVulpes.proxy.getLocalizedString("msg.itemsatellite.pwr") + powerStorage + : ChatFormatting.RED + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.nopwr")); } - if (SatelliteProperties.Property.POWER_GEN.isOfType(properties.getPropertyFlag())) { - if ((powerGeneration = properties.getPowerGeneration()) > 0) - list.add(LibVulpes.proxy.getLocalizedString("msg.itemsatellite.pwrgen") + powerGeneration); - else - list.add(ChatFormatting.RED + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.nopwrgen")); + if (SatelliteProperties.Property.POWER_GEN.isOfType(props.getPropertyFlag())) { + powerGeneration = props.getPowerGeneration(); + list.add((powerGeneration > 0) + ? LibVulpes.proxy.getLocalizedString("msg.itemsatellite.pwrgen") + powerGeneration + : ChatFormatting.RED + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.nopwrgen")); } - if (SatelliteProperties.Property.DATA.isOfType(properties.getPropertyFlag())) { - if ((dataStorage = properties.getMaxDataStorage()) > 0) - list.add(LibVulpes.proxy.getLocalizedString("msg.itemsatellite.data") + ZUtils.formatNumber(dataStorage)); - else - list.add(ChatFormatting.YELLOW + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.nodata")); + if (SatelliteProperties.Property.DATA.isOfType(props.getPropertyFlag())) { + dataStorage = props.getMaxDataStorage(); + list.add((dataStorage > 0) + ? LibVulpes.proxy.getLocalizedString("msg.itemsatellite.data") + ZUtils.formatNumber(dataStorage) + : ChatFormatting.YELLOW + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.nodata")); } + // Data gen line only meaningful when the satellite has BOTH power generation and data storage. + int pg = props.getPowerGeneration(); + int maxData = props.getMaxDataStorage(); - if ((weight = properties.getWeight()) > 0) - list.add(LibVulpes.proxy.getLocalizedString("msg.itemsatellite.weight") + weight); - else - list.add(ChatFormatting.YELLOW + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.noweight")); + if (base instanceof SatelliteData && pg > 0 && maxData > 0) { + list.add(makeDataGenLine(calcDataPerSecond(pg))); + } + weight = props.getWeight(); + list.add((weight > 0f) + ? LibVulpes.proxy.getLocalizedString("msg.itemsatellite.weight") + weight + : ChatFormatting.YELLOW + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.noweight")); + return; + } + + // --- Preview for unassembled chassis --- + EmbeddedInventory inv = readInvFromNBT(stack); - } else { + boolean hasParts = false; + for (int i = CORE_SLOT; i <= LAST_MOD_SLOT; i++) { + if (!inv.getStackInSlot(i).isEmpty()) { hasParts = true; break; } + } + if (!hasParts) { list.add(ChatFormatting.RED + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.empty")); + return; } + int flags = 0; + int powerGen = 0, powerStor = 0, dataMax = 0; + float weight = 0f; + boolean showDataGenPreview = false; + + // Core first: flags + preview type name (no weight from core) + ItemStack core = inv.getStackInSlot(CORE_SLOT); + + String satType = ""; + SatelliteBase satBase = null; + + if (!core.isEmpty()) { + SatelliteProperties cp = SatelliteRegistry.getSatelliteProperty(core); + if (cp != null) { + flags |= cp.getPropertyFlag(); + satType = cp.getSatelliteType() == null ? "" : cp.getSatelliteType(); + satBase = SatelliteRegistry.getNewSatellite(satType); + + if (satBase != null) { + // Show same display name users will see after assembly + list.add(satBase.getName()); + } + } + } + + // Preview: show for "type empty" OR data collectors + showDataGenPreview = satType.isEmpty() || (satBase instanceof SatelliteData); + + + // Modules: stats + weight + for (int i = FIRST_MOD_SLOT; i <= LAST_MOD_SLOT; i++) { + ItemStack s = inv.getStackInSlot(i); + if (s.isEmpty()) continue; + + SatelliteProperties p = SatelliteRegistry.getSatelliteProperty(s); + if (p != null) { + flags |= p.getPropertyFlag(); + int f = p.getPropertyFlag(); + if (f == SatelliteProperties.Property.POWER_GEN.getFlag()) + powerGen += p.getPowerGeneration(); + else if (f == SatelliteProperties.Property.BATTERY.getFlag()) + powerStor += p.getPowerStorage(); + else if (f == SatelliteProperties.Property.DATA.getFlag()) + dataMax += p.getMaxDataStorage(); + } + weight += zmaster587.advancedRocketry.util.WeightEngine.INSTANCE.getWeight(s); + } + + // Match assembly semantics: base buffer is always present + powerStor += 720; + + // Always show power storage in preview (even if no battery modules are installed) + list.add(LibVulpes.proxy.getLocalizedString("msg.itemsatellite.pwr") + powerStor); + + if (SatelliteProperties.Property.POWER_GEN.isOfType(flags)) { + list.add((powerGen > 0) + ? LibVulpes.proxy.getLocalizedString("msg.itemsatellite.pwrgen") + powerGen + : ChatFormatting.RED + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.nopwrgen")); + } + if (SatelliteProperties.Property.DATA.isOfType(flags)) { + list.add((dataMax > 0) + ? LibVulpes.proxy.getLocalizedString("msg.itemsatellite.data") + ZUtils.formatNumber(dataMax) + : ChatFormatting.YELLOW + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.nodata")); + } + // Preview data gen line (same semantics + same formula as runtime) + if (showDataGenPreview && powerGen > 0 && dataMax > 0) { + list.add(makeDataGenLine(calcDataPerSecond(powerGen))); + } + if (weight > 0f) { + list.add(LibVulpes.proxy.getLocalizedString("msg.itemsatellite.weight") + weight); + } + + // Footer LAST + list.add(ChatFormatting.RED + LibVulpes.proxy.getLocalizedString("msg.itemsatellite.unassembled")); } + } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemSatelliteIdentificationChip.java b/src/main/java/zmaster587/advancedRocketry/item/ItemSatelliteIdentificationChip.java index dc39132a1..c3cb9a4c6 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemSatelliteIdentificationChip.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemSatelliteIdentificationChip.java @@ -61,6 +61,8 @@ public void setSatellite(@Nonnull ItemStack stack, SatelliteBase satellite) { nbt.setString("satelliteName", satellite.getName()); nbt.setInteger("dimId", satellite.getDimensionId()); nbt.setLong("satelliteId", satellite.getId()); + + stack.setTagCompound(nbt); } /** @@ -127,7 +129,13 @@ public void addInformation(@Nonnull ItemStack stack, World player, List int worldId = getWorldId(stack); long satId = SatelliteRegistry.getSatelliteId(stack); - String satelliteName = getSatelliteName(stack); + String satelliteNameKey = getSatelliteName(stack); + String satelliteName = satelliteNameKey; + + // Translate if it's a lang key; if missing, translateToLocal returns the key + if (!satelliteNameKey.isEmpty()) { + satelliteName = net.minecraft.util.text.translation.I18n.translateToLocal(satelliteNameKey); + } if (satId != -1) { diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemSealDetector.java b/src/main/java/zmaster587/advancedRocketry/item/ItemSealDetector.java index b01a05aa2..cace938ce 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemSealDetector.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemSealDetector.java @@ -1,7 +1,12 @@ package zmaster587.advancedRocketry.item; +import java.util.List; + +import javax.annotation.Nullable; + import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -13,6 +18,9 @@ import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; import net.minecraftforge.fluids.IFluidBlock; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.util.SealableBlockHandler; /** @@ -20,7 +28,6 @@ * Created by Dark(DarkGuardsman, Robert) on 1/6/2016. */ public class ItemSealDetector extends Item { - //TODO make consume power? @Override public ActionResult onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand hand) { @@ -53,4 +60,10 @@ public EnumActionResult onItemUse(EntityPlayer player, return EnumActionResult.SUCCESS; } + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.sealdetector", insertAt); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemStationChip.java b/src/main/java/zmaster587/advancedRocketry/item/ItemStationChip.java index 0380cd43b..671c79531 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemStationChip.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemStationChip.java @@ -359,11 +359,11 @@ public void addInformation(@Nonnull ItemStack stack, World player, List LandingLocation loc = getTakeoffCoords(stack, spaceObject.getOrbitingPlanetId()); if (loc != null) { Vector3F vec = loc.location; - list.add("Name: " + loc.name); + list.add(LibVulpes.proxy.getLocalizedString("tooltip.advancedrocketry.stationchip.namelabel") + loc.name); list.add("X: " + vec.x); list.add("Z: " + vec.z); } else { - list.add("Name: N/A"); + list.add(LibVulpes.proxy.getLocalizedString("tooltip.advancedrocketry.stationchip.namelabel") + "N/A"); list.add("X: N/A"); list.add("Z: N/A"); } @@ -372,11 +372,11 @@ public void addInformation(@Nonnull ItemStack stack, World player, List LandingLocation loc = getTakeoffCoords(stack, player.provider.getDimension()); if (loc != null) { Vector3F vec = loc.location; - list.add("Name: " + loc.name); + list.add(LibVulpes.proxy.getLocalizedString("tooltip.advancedrocketry.stationchip.namelabel") + loc.name); list.add("X: " + vec.x); list.add("Z: " + vec.z); } else { - list.add("Name: N/A"); + list.add(LibVulpes.proxy.getLocalizedString("tooltip.advancedrocketry.stationchip.namelabel") + "N/A"); list.add("X: N/A"); list.add("Z: N/A"); } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemThermite.java b/src/main/java/zmaster587/advancedRocketry/item/ItemThermite.java index a17a52453..9b0368631 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemThermite.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemThermite.java @@ -1,9 +1,17 @@ package zmaster587.advancedRocketry.item; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zmaster587.advancedRocketry.client.TooltipInjector; + +import java.util.List; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ItemThermite extends Item { @@ -11,5 +19,11 @@ public class ItemThermite extends Item { public int getItemBurnTime(@Nonnull ItemStack itemStack) { return 6000; } - + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.thermite", insertAt); + } + } diff --git a/src/main/java/zmaster587/advancedRocketry/item/ItemWeatherController.java b/src/main/java/zmaster587/advancedRocketry/item/ItemWeatherController.java index a3360f947..9e10b0079 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemWeatherController.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemWeatherController.java @@ -56,30 +56,36 @@ public List getModules(int id, EntityPlayer player) { } @Override - public void addInformation(@Nonnull ItemStack stack, World player, List list, ITooltipFlag arg5) { + public void addInformation(@Nonnull ItemStack stack, World world, List list, ITooltipFlag flag) { - SatelliteBase sat = SatelliteRegistry.getSatellite(stack); + // If unprogrammed, let the superclass handle the "unprogrammed" tooltip + if (!stack.hasTagCompound()) { + super.addInformation(stack, world, list, flag); + return; + } + SatelliteBase sat = SatelliteRegistry.getSatellite(stack); SatelliteWeatherController mapping = null; + if (sat instanceof SatelliteWeatherController) mapping = (SatelliteWeatherController) sat; - if (!stack.hasTagCompound()) - list.add(LibVulpes.proxy.getLocalizedString("msg.unprogrammed")); - else if (mapping == null) + if (mapping == null) { list.add(LibVulpes.proxy.getLocalizedString("msg.biomechanger.nosat")); - else if (mapping.getDimensionId() == player.provider.getDimension()) { + } else if (mapping.getDimensionId() == world.provider.getDimension()) { list.add(LibVulpes.proxy.getLocalizedString("msg.connected")); if (mapping.mode_id == 0) - list.add("mode: rain - Fills small basins in the terrain with water"); + list.add(LibVulpes.proxy.getLocalizedString("tooltip.advancedrocketry.weathercontrollerremote.mode.rain")); if (mapping.mode_id == 1) - list.add("mode: dry - Drys all water in a radius of 16"); + list.add(LibVulpes.proxy.getLocalizedString("tooltip.advancedrocketry.weathercontrollerremote.mode.dry")); if (mapping.mode_id == 2) - list.add("mode: flood - Floods area with a radius of 16 with water"); - } else + list.add(LibVulpes.proxy.getLocalizedString("tooltip.advancedrocketry.weathercontrollerremote.mode.flood")); + } else { list.add(LibVulpes.proxy.getLocalizedString("msg.notconnected")); + } - super.addInformation(stack, player, list, arg5); + // Still let the parent add its usual info + super.addInformation(stack, world, list, flag); } diff --git a/src/main/java/zmaster587/advancedRocketry/item/components/ItemJetpack.java b/src/main/java/zmaster587/advancedRocketry/item/components/ItemJetpack.java index fc219584b..1e41e822d 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/components/ItemJetpack.java +++ b/src/main/java/zmaster587/advancedRocketry/item/components/ItemJetpack.java @@ -2,6 +2,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.EntityEquipmentSlot; @@ -22,6 +23,7 @@ import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.AdvancedRocketryFluids; import zmaster587.advancedRocketry.api.AdvancedRocketryItems; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.event.RocketEventHandler; import zmaster587.advancedRocketry.inventory.TextureResources; import zmaster587.libVulpes.api.IArmorComponent; @@ -32,6 +34,8 @@ import zmaster587.libVulpes.util.InputSyncHandler; import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import java.util.List; public class ItemJetpack extends Item implements IArmorComponent, IJetPack { @@ -345,4 +349,12 @@ private enum MODES { NORMAL, HOVER } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.jetpack", insertAt); + } + } diff --git a/src/main/java/zmaster587/advancedRocketry/item/components/ItemPressureTank.java b/src/main/java/zmaster587/advancedRocketry/item/components/ItemPressureTank.java index 0f727dc9c..84d8edfc9 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/components/ItemPressureTank.java +++ b/src/main/java/zmaster587/advancedRocketry/item/components/ItemPressureTank.java @@ -1,7 +1,9 @@ package zmaster587.advancedRocketry.item.components; import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.client.resources.I18n; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.EntityEquipmentSlot; @@ -9,12 +11,14 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.DamageSource; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import org.lwjgl.input.Keyboard; import zmaster587.advancedRocketry.capability.TankCapabilityItemStack; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.api.IArmorComponent; @@ -23,6 +27,8 @@ import zmaster587.libVulpes.util.FluidUtils; import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import java.util.List; public class ItemPressureTank extends ItemIngredient implements IArmorComponent { @@ -34,26 +40,47 @@ public class ItemPressureTank extends ItemIngredient implements IArmorComponent public ItemPressureTank(int number, int capacity) { super(number); this.capacity = capacity; - this.maxStackSize = 1; + this.maxStackSize = 8; } - + + @SideOnly(Side.CLIENT) @Override - public void addInformation(@Nonnull ItemStack stack, World player, List list, ITooltipFlag bool) { - super.addInformation(stack, player, list, bool); - - FluidStack fluidStack = FluidUtils.getFluidForItem(stack); + public void addInformation(@Nonnull ItemStack stack, @Nullable World world, + List list, ITooltipFlag flag) { + super.addInformation(stack, world, list, flag); + + final int capMb = Math.max(0, getCapacity(stack)); + final net.minecraftforge.fluids.FluidStack fs = zmaster587.libVulpes.util.FluidUtils.getFluidForItem(stack); + + final String fluidName = (fs != null && fs.getFluid() != null) ? fs.getLocalizedName() : I18n.format("tooltip.advancedrocketry.fluidtank.empty"); + final int amount = (fs != null) ? fs.amount : 0; + + // Match main tank style + list.add(I18n.format("tooltip.advancedrocketry.itemdata.header")); + list.add(I18n.format("tooltip.advancedrocketry.fluidtank.fluid") + fluidName); + list.add(I18n.format("tooltip.advancedrocketry.fluidtank.level") + amount + "/" + capMb + " mB"); + + // SHIFT block + if (GuiScreen.isShiftKeyDown()) { + list.add(TextFormatting.GRAY + I18n.format("tooltip.advancedrocketry.pressuretank.shift.1")); + } else if (I18n.hasKey("tooltip.advancedrocketry.hold_shift")) { + list.add(TextFormatting.DARK_GRAY.toString() + TextFormatting.ITALIC + + I18n.format("tooltip.advancedrocketry.hold_shift")); + } - if (fluidStack == null) { - list.add(LibVulpes.proxy.getLocalizedString("msg.empty")); - } else { - list.add(fluidStack.getLocalizedName() + ": " + fluidStack.amount); + // ALT block (independent of SHIFT) + if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU)) { + list.add(TextFormatting.DARK_GRAY + I18n.format("tooltip.advancedrocketry.pressuretank.alt.1")); + list.add(TextFormatting.DARK_GRAY + I18n.format("tooltip.advancedrocketry.pressuretank.alt.2")); + } else if (I18n.hasKey("tooltip.advancedrocketry.hold_alt")) { + list.add(TextFormatting.DARK_GRAY.toString() + TextFormatting.ITALIC + + I18n.format("tooltip.advancedrocketry.hold_alt")); } } @Override public void onTick(World world, EntityPlayer player, @Nonnull ItemStack armorStack, IInventory inv, @Nonnull ItemStack componentStack) { - } @Override @@ -90,12 +117,11 @@ public boolean isAllowedInSlot(@Nonnull ItemStack stack, EntityEquipmentSlot slo @SideOnly(Side.CLIENT) public void renderScreen(@Nonnull ItemStack componentStack, List modules, RenderGameOverlayEvent event, Gui gui) { // TODO Auto-generated method stub - } + @Override public ICapabilityProvider initCapabilities(@Nonnull ItemStack stack, NBTTagCompound nbt) { return new TankCapabilityItemStack(stack, getCapacity(stack)); } - } diff --git a/src/main/java/zmaster587/advancedRocketry/item/tools/ItemBasicLaserGun.java b/src/main/java/zmaster587/advancedRocketry/item/tools/ItemBasicLaserGun.java index 2e50ef406..ca58000fe 100644 --- a/src/main/java/zmaster587/advancedRocketry/item/tools/ItemBasicLaserGun.java +++ b/src/main/java/zmaster587/advancedRocketry/item/tools/ItemBasicLaserGun.java @@ -5,6 +5,7 @@ import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; @@ -17,11 +18,15 @@ import net.minecraft.util.math.*; import net.minecraft.util.math.RayTraceResult.Type; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.AdvancedRocketry; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.util.AudioRegistry; import zmaster587.libVulpes.LibVulpes; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import java.util.List; import java.util.WeakHashMap; @@ -249,4 +254,12 @@ public ActionResult onItemRightClick(World worldIn, EntityPlayer play } return new ActionResult<>(EnumActionResult.PASS, stack); } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack stack, @Nullable World world, List tooltip, ITooltipFlag flag) { + int insertAt = TooltipInjector.computeInsertIndex(tooltip, flag.isAdvanced()); + TooltipInjector.renderShiftAlt(stack, tooltip, "tooltip.advancedrocketry.lasergun", insertAt); + } + } diff --git a/src/main/java/zmaster587/advancedRocketry/mission/MissionGasCollection.java b/src/main/java/zmaster587/advancedRocketry/mission/MissionGasCollection.java index 6c94b3d21..6cb80d0cc 100644 --- a/src/main/java/zmaster587/advancedRocketry/mission/MissionGasCollection.java +++ b/src/main/java/zmaster587/advancedRocketry/mission/MissionGasCollection.java @@ -22,7 +22,6 @@ public class MissionGasCollection extends MissionResourceCollection { - private Fluid gasFluid; public MissionGasCollection() { @@ -42,14 +41,49 @@ public String getName() { @Override public void onMissionComplete() { + Object ipObj = rocketStats.getStatTag("intakePower"); + int ip = (ipObj instanceof Number) ? Math.max(0, ((Number) ipObj).intValue()) : 0; + + if (ip > 0 && gasFluid != null) { + final Fluid type = gasFluid; + + // Planned harvest written by the rocket at launch + final boolean hasPlanned = missionPersistantNBT.hasKey("plannedHarvestMb"); + final long planned = hasPlanned ? Math.max(0L, missionPersistantNBT.getLong("plannedHarvestMb")) : -1L; + + // Config + final boolean infinite = ARConfiguration.getCurrentConfig().gasHarvestInfinite; + final double mult = Math.max(0.0, ARConfiguration.getCurrentConfig().gasHarvestAmountMultiplier); + final long basePerMission = 64_000L; // mB + + long remaining; + if (hasPlanned) { + remaining = Math.min(Integer.MAX_VALUE, planned); + } else { + remaining = infinite + ? Integer.MAX_VALUE + : Math.min(Integer.MAX_VALUE, Math.round(basePerMission * mult)); + } + + - if ((int) rocketStats.getStatTag("intakePower") > 0 && gasFluid != null) { - Fluid type = gasFluid;//FluidRegistry.getFluid("hydrogen"); - //Fill gas tanks for (TileEntity tile : this.rocketStorage.getFluidTiles()) { - tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null).fill(new FluidStack(type, 64000), true); + net.minecraftforge.fluids.capability.IFluidHandler handler = + tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); + if (handler == null) continue; + + if (remaining <= 0) break; + + int want = (int)Math.min(Integer.MAX_VALUE, remaining); + int couldTake = handler.fill(new FluidStack(type, want), false); // simulate + if (couldTake > 0) { + int filled = handler.fill(new FluidStack(type, couldTake), true); + remaining -= Math.max(0, filled); + } } } + + World world = DimensionManager.getWorld(launchDimension); if (world == null) { @@ -89,12 +123,19 @@ public void onMissionComplete() { @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); - nbt.setString("gas", gasFluid.getName()); + if (gasFluid != null) { + nbt.setString("gas", gasFluid.getName()); + } } + public net.minecraftforge.fluids.Fluid getGasFluid() { + return gasFluid; + } + @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); - gasFluid = FluidRegistry.getFluid(nbt.getString("gas")); + String name = nbt.getString("gas"); + gasFluid = name != null && !name.isEmpty() ? FluidRegistry.getFluid(name) : null; } } diff --git a/src/main/java/zmaster587/advancedRocketry/mission/MissionOreMining.java b/src/main/java/zmaster587/advancedRocketry/mission/MissionOreMining.java index 3e180f4fa..1d2e690b7 100644 --- a/src/main/java/zmaster587/advancedRocketry/mission/MissionOreMining.java +++ b/src/main/java/zmaster587/advancedRocketry/mission/MissionOreMining.java @@ -9,6 +9,7 @@ import net.minecraftforge.common.DimensionManager; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.wrapper.InvWrapper; import zmaster587.advancedRocketry.AdvancedRocketry; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.AdvancedRocketryItems; @@ -24,6 +25,7 @@ import java.util.LinkedList; import java.util.List; + public class MissionOreMining extends MissionResourceCollection { @@ -34,8 +36,61 @@ public MissionOreMining() { public MissionOreMining(long l, EntityRocket entityRocket, LinkedList connectedInfrastructure) { super(l, entityRocket, connectedInfrastructure); + + // Persist asteroid metadata for the monitor UI + try { + if (rocketStorage != null && rocketStorage.getGuidanceComputer() != null) { + ItemStack chip = rocketStorage.getGuidanceComputer().getStackInSlot(0); + if (!chip.isEmpty() && chip.getItem() instanceof ItemAsteroidChip) { + ItemAsteroidChip ac = (ItemAsteroidChip) chip.getItem(); + + String type = ac.getType(chip); + Long uuid = ac.getUUID(chip); + + if (type != null && !type.isEmpty()) + missionPersistantNBT.setString("asteroidType", type); + if (uuid != null) + missionPersistantNBT.setLong("asteroidUUID", uuid); + } + } + } catch (Throwable t) { + // leave fields unset; GUI will show defaults + } + } + + @javax.annotation.Nullable + private static IItemHandler getItemHandler(TileEntity tile) { + if (tile == null) return null; + + // Prefer capability (modded inventories) + if (tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)) { + IItemHandler h = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); + if (h != null) return h; + } + for (EnumFacing face : EnumFacing.VALUES) { + if (tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, face)) { + IItemHandler h = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, face); + if (h != null) return h; + } + } + + // Vanilla fallback (and “treat sided inventories like normal”, closest to old behavior) + if (tile instanceof IInventory) { + return new InvWrapper((IInventory) tile); + } + + return null; } + public String getAsteroidTypeOrEmpty() { + return (missionPersistantNBT != null && missionPersistantNBT.hasKey("asteroidType")) + ? missionPersistantNBT.getString("asteroidType") : ""; + } + @javax.annotation.Nullable + public Long getAsteroidUUIDOrNull() { + return (missionPersistantNBT != null && missionPersistantNBT.hasKey("asteroidUUID")) + ? missionPersistantNBT.getLong("asteroidUUID") : null; + } @Override public void onMissionComplete() { @@ -83,30 +138,31 @@ public void onMissionComplete() { totalStacksList.add(stack2); } //} - entry.stack.setCount(entry.stack.getCount() % entry.stack.getMaxStackSize()); - totalStacksList.add(entry.stack); + int rem = entry.stack.getCount() % entry.stack.getMaxStackSize(); + if (rem > 0) { + entry.stack.setCount(rem); + totalStacksList.add(entry.stack); + } } stacks = new ItemStack[totalStacksList.size()]; totalStacksList.toArray(stacks); - for (int i = 0, g = 0; i < rocketStorage.getInventoryTiles().size(); i++) { - if (rocketStorage.getInventoryTiles().get(i).hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP)) { - IItemHandler capabilityItemHandle = rocketStorage.getInventoryTiles().get(i).getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP); + for (int g = 0; g < stacks.length; g++) { + ItemStack remaining = stacks[g].copy(); + if (remaining.isEmpty()) continue; - for (int offset = 0; offset < capabilityItemHandle.getSlots() && g < stacks.length; offset++, g++) { - if (capabilityItemHandle.getStackInSlot(offset).isEmpty()) - capabilityItemHandle.insertItem(offset, stacks[g], false); - } - } else { - IInventory tile = (IInventory) rocketStorage.getInventoryTiles().get(i); + for (int i = 0; i < rocketStorage.getInventoryTiles().size() && !remaining.isEmpty(); i++) { + TileEntity te = rocketStorage.getInventoryTiles().get(i); + IItemHandler handler = getItemHandler(te); + if (handler == null) continue; - - for (int offset = 0; offset < tile.getSizeInventory() && g < stacks.length; offset++, g++) { - if (tile.getStackInSlot(offset).isEmpty()) - tile.setInventorySlotContents(offset, stacks[g]); + for (int slot = 0; slot < handler.getSlots() && !remaining.isEmpty(); slot++) { + remaining = handler.insertItem(slot, remaining, false); } } + + // Any leftover is intentionally voided } } } diff --git a/src/main/java/zmaster587/advancedRocketry/mission/MissionResourceCollection.java b/src/main/java/zmaster587/advancedRocketry/mission/MissionResourceCollection.java index 18fe4e55c..8126abccf 100644 --- a/src/main/java/zmaster587/advancedRocketry/mission/MissionResourceCollection.java +++ b/src/main/java/zmaster587/advancedRocketry/mission/MissionResourceCollection.java @@ -37,6 +37,7 @@ public abstract class MissionResourceCollection extends SatelliteBase implements public MissionResourceCollection() { infrastructureCoords = new LinkedList<>(); + missionPersistantNBT = new NBTTagCompound(); } public MissionResourceCollection(long duration, EntityRocket entity, LinkedList infrastructureCoords) { @@ -48,6 +49,10 @@ public MissionResourceCollection(long duration, EntityRocket entity, LinkedList< startWorldTime = DimensionManager.getWorld(0).getTotalWorldTime(); this.duration = duration; + if (this.duration <= 0L) { + this.duration = 1L; // at least 1 tick + } + this.launchDimension = entity.world.provider.getDimension(); rocketStorage = entity.storage; rocketStats = entity.stats; @@ -62,8 +67,16 @@ public MissionResourceCollection(long duration, EntityRocket entity, LinkedList< this.infrastructureCoords.add(new HashedBlockPosition(((TileEntity) tile).getPos())); } + public long getPlannedHarvestMbOrDefault() { + if (missionPersistantNBT != null && missionPersistantNBT.hasKey("plannedHarvestMb")) { + return Math.max(0L, missionPersistantNBT.getLong("plannedHarvestMb")); + } + return -1L; // means "unknown/not provided" + } + @Override public double getProgress(World world) { + if (duration <= 0L) return 1.0d; return Math.max((AdvancedRocketry.proxy.getWorldTimeUniversal(0) - startWorldTime) / (double) duration, 0); } @@ -143,7 +156,8 @@ public void writeToNBT(NBTTagCompound nbt) { public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); - missionPersistantNBT = nbt.getCompoundTag("persist"); + missionPersistantNBT = nbt.hasKey("persist") ? nbt.getCompoundTag("persist") : new NBTTagCompound(); + rocketStats = new StatsRocket(); rocketStats.readFromNBT(nbt.getCompoundTag("rocketStats")); @@ -184,5 +198,4 @@ public void unlinkInfrastructure(IInfrastructure tile) { HashedBlockPosition pos = new HashedBlockPosition(((TileEntity) tile).getPos()); infrastructureCoords.remove(pos); } - } diff --git a/src/main/java/zmaster587/advancedRocketry/network/PacketStationUpdate.java b/src/main/java/zmaster587/advancedRocketry/network/PacketStationUpdate.java index 21eb199fd..28182d281 100644 --- a/src/main/java/zmaster587/advancedRocketry/network/PacketStationUpdate.java +++ b/src/main/java/zmaster587/advancedRocketry/network/PacketStationUpdate.java @@ -75,6 +75,7 @@ public void write(ByteBuf out) { Logger.getLogger("advancedRocketry").warning("Dimension " + stationNumber + " has thrown an exception trying to write NBT, deleting!"); DimensionManager.getInstance().deleteDimension(stationNumber); } + break; default: } } @@ -128,7 +129,8 @@ public void read(ByteBuf in) { @Override public void executeClient(EntityPlayer thePlayer) { spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStation(stationNumber); - + if (spaceObject == null) return; + switch (type) { case DEST_ORBIT_UPDATE: spaceObject.setDestOrbitingBody(destOrbitingBody); @@ -140,14 +142,12 @@ public void executeClient(EntityPlayer thePlayer) { if (spaceObject instanceof SpaceStationObject) ((SpaceStationObject) spaceObject).setFuelAmount(fuel); break; - case ROTANGLE_UPDATE: - spaceObject.setRotation(rx, EnumFacing.EAST); - spaceObject.setRotation(ry, EnumFacing.UP); - spaceObject.setRotation(rz, EnumFacing.NORTH); - spaceObject.setDeltaRotation(drx, EnumFacing.EAST); - spaceObject.setDeltaRotation(dry, EnumFacing.UP); - spaceObject.setDeltaRotation(drz, EnumFacing.NORTH); + case ROTANGLE_UPDATE: { + ((SpaceStationObject) spaceObject).applyRemoteRotationState( + rx, ry, rz, drx, dry, drz + ); break; + } case SIGNAL_WHITE_BURST: PlanetEventHandler.runBurst(Minecraft.getMinecraft().world.getTotalWorldTime() + 20, 20); break; diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/AsmHook.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/AsmHook.java deleted file mode 100644 index 2029b25c1..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/AsmHook.java +++ /dev/null @@ -1,810 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -import java.util.ArrayList; -import java.util.List; - -import static org.objectweb.asm.Opcodes.*; -import static org.objectweb.asm.Type.*; - -/** - * Class places one hook to one method - * Terminology: - * hook - the call of your hook-method from extra code - * targetMethod - method, where the hook would be injected - * targetClass - class, whose method would be hooked - * hookMethod - YOUR static method, which represents the hook - * hookClass - class with HOOK-method - */ -public class AsmHook implements Cloneable, Comparable { - - private String targetClassName; // через точки - private String targetMethodName; - private List targetMethodParameters = new ArrayList(2); - private Type targetMethodReturnType; //если не задано, то не проверяется - - private String hooksClassName; // через точки - private String hookMethodName; - // -1 - значение return - private List transmittableVariableIds = new ArrayList(2); - private List hookMethodParameters = new ArrayList(2); - private Type hookMethodReturnType = Type.VOID_TYPE; - private boolean hasReturnValueParameter; // если в хук-метод передается значение из return - - private ReturnCondition returnCondition = ReturnCondition.NEVER; - private ReturnValue returnValue = ReturnValue.VOID; - private Object primitiveConstant; - - private HookInjectorFactory injectorFactory = ON_ENTER_FACTORY; - private HookPriority priority = HookPriority.NORMAL; - - public static final HookInjectorFactory ON_ENTER_FACTORY = HookInjectorFactory.MethodEnter.INSTANCE; - public static final HookInjectorFactory ON_EXIT_FACTORY = HookInjectorFactory.MethodExit.INSTANCE; - - // может быть без возвращаемого типа - private String targetMethodDescription; - private String hookMethodDescription; - private String returnMethodName; - // может быть без возвращаемого типа - private String returnMethodDescription; - - private boolean createMethod; - private boolean isMandatory; - - protected String getTargetClassName() { - return targetClassName; - } - - private String getTargetClassInternalName() { - return targetClassName.replace('.', '/'); - } - - private String getHookClassInternalName() { - return hooksClassName.replace('.', '/'); - } - - protected boolean isTargetMethod(String name, String desc) { - return (targetMethodReturnType == null && desc.startsWith(targetMethodDescription) || - desc.equals(targetMethodDescription)) && name.equals(targetMethodName); - } - - protected boolean getCreateMethod() { - return createMethod; - } - - protected boolean isMandatory() { - return isMandatory; - } - - protected HookInjectorFactory getInjectorFactory() { - return injectorFactory; - } - - private boolean hasHookMethod() { - return hookMethodName != null && hooksClassName != null; - } - - protected void createMethod(HookInjectorClassVisitor classVisitor) { - ClassMetadataReader.MethodReference superMethod = classVisitor.transformer.classMetadataReader - .findVirtualMethod(getTargetClassInternalName(), targetMethodName, targetMethodDescription); - // юзаем название суперметода, потому что findVirtualMethod может вернуть метод с другим названием - MethodVisitor mv = classVisitor.visitMethod(Opcodes.ACC_PUBLIC, - superMethod == null ? targetMethodName : superMethod.name, targetMethodDescription, null, null); - if (mv instanceof HookInjectorMethodVisitor) { - HookInjectorMethodVisitor inj = (HookInjectorMethodVisitor) mv; - inj.visitCode(); - inj.visitLabel(new Label()); - if (superMethod == null) { - injectDefaultValue(inj, targetMethodReturnType); - } else { - injectSuperCall(inj, superMethod); - } - injectReturn(inj, targetMethodReturnType); - inj.visitLabel(new Label()); - inj.visitMaxs(0, 0); - inj.visitEnd(); - } else { - throw new IllegalArgumentException("Hook injector not created"); - } - } - - protected void inject(HookInjectorMethodVisitor inj) { - Type targetMethodReturnType = inj.methodType.getReturnType(); - - // сохраняем значение, которое было передано return в локальную переменную - int returnLocalId = -1; - if (hasReturnValueParameter) { - returnLocalId = inj.newLocal(targetMethodReturnType); - inj.visitVarInsn(targetMethodReturnType.getOpcode(54), returnLocalId); //storeLocal - } - - // вызываем хук-метод - int hookResultLocalId = -1; - if (hasHookMethod()) { - injectInvokeStatic(inj, returnLocalId, hookMethodName, hookMethodDescription); - - if (returnValue == ReturnValue.HOOK_RETURN_VALUE || returnCondition.requiresCondition) { - hookResultLocalId = inj.newLocal(hookMethodReturnType); - inj.visitVarInsn(hookMethodReturnType.getOpcode(54), hookResultLocalId); //storeLocal - } - } - - // вызываем return - if (returnCondition != ReturnCondition.NEVER) { - Label label = inj.newLabel(); - - // вставляем GOTO-переход к label'у после вызова return - if (returnCondition != ReturnCondition.ALWAYS) { - inj.visitVarInsn(hookMethodReturnType.getOpcode(21), hookResultLocalId); //loadLocal - if (returnCondition == ReturnCondition.ON_TRUE) { - inj.visitJumpInsn(IFEQ, label); - } else if (returnCondition == ReturnCondition.ON_NULL) { - inj.visitJumpInsn(IFNONNULL, label); - } else if (returnCondition == ReturnCondition.ON_NOT_NULL) { - inj.visitJumpInsn(IFNULL, label); - } - } - - // вставляем в стак значение, которое необходимо вернуть - if (returnValue == ReturnValue.NULL) { - inj.visitInsn(Opcodes.ACONST_NULL); - } else if (returnValue == ReturnValue.PRIMITIVE_CONSTANT) { - inj.visitLdcInsn(primitiveConstant); - } else if (returnValue == ReturnValue.HOOK_RETURN_VALUE) { - inj.visitVarInsn(hookMethodReturnType.getOpcode(21), hookResultLocalId); //loadLocal - } else if (returnValue == ReturnValue.ANOTHER_METHOD_RETURN_VALUE) { - String returnMethodDescription = this.returnMethodDescription; - // если не был определён заранее нужный возвращаемый тип, то добавляем его к описанию - if (returnMethodDescription.endsWith(")")) { - returnMethodDescription += targetMethodReturnType.getDescriptor(); - } - injectInvokeStatic(inj, returnLocalId, returnMethodName, returnMethodDescription); - } - - // вызываем return - injectReturn(inj, targetMethodReturnType); - - // вставляем label, к которому идет GOTO-переход - inj.visitLabel(label); - } - - //кладем в стек значение, которое шло в return - if (hasReturnValueParameter) { - injectLoad(inj, targetMethodReturnType, returnLocalId); - } - } - - private void injectLoad(HookInjectorMethodVisitor inj, Type parameterType, int variableId) { - int opcode; - if (parameterType == INT_TYPE || parameterType == BYTE_TYPE || parameterType == CHAR_TYPE || - parameterType == BOOLEAN_TYPE || parameterType == SHORT_TYPE) { - opcode = ILOAD; - } else if (parameterType == LONG_TYPE) { - opcode = LLOAD; - } else if (parameterType == FLOAT_TYPE) { - opcode = FLOAD; - } else if (parameterType == DOUBLE_TYPE) { - opcode = DLOAD; - } else { - opcode = ALOAD; - } - inj.visitVarInsn(opcode, variableId); - } - - private void injectSuperCall(HookInjectorMethodVisitor inj, ClassMetadataReader.MethodReference method) { - int variableId = 0; - for (int i = 0; i <= targetMethodParameters.size(); i++) { - Type parameterType = i == 0 ? TypeHelper.getType(targetClassName) : targetMethodParameters.get(i - 1); - injectLoad(inj, parameterType, variableId); - if (parameterType.getSort() == Type.DOUBLE || parameterType.getSort() == Type.LONG) { - variableId += 2; - } else { - variableId++; - } - } - inj.visitMethodInsn(INVOKESPECIAL, method.owner, method.name, method.desc, false); - } - - private void injectDefaultValue(HookInjectorMethodVisitor inj, Type targetMethodReturnType) { - switch (targetMethodReturnType.getSort()) { - case Type.VOID: - break; - case Type.BOOLEAN: - case Type.CHAR: - case Type.BYTE: - case Type.SHORT: - case Type.INT: - inj.visitInsn(Opcodes.ICONST_0); - break; - case Type.FLOAT: - inj.visitInsn(Opcodes.FCONST_0); - break; - case Type.LONG: - inj.visitInsn(Opcodes.LCONST_0); - break; - case Type.DOUBLE: - inj.visitInsn(Opcodes.DCONST_0); - break; - default: - inj.visitInsn(Opcodes.ACONST_NULL); - break; - } - } - - private void injectReturn(HookInjectorMethodVisitor inj, Type targetMethodReturnType) { - if (targetMethodReturnType == INT_TYPE || targetMethodReturnType == SHORT_TYPE || - targetMethodReturnType == BOOLEAN_TYPE || targetMethodReturnType == BYTE_TYPE - || targetMethodReturnType == CHAR_TYPE) { - inj.visitInsn(IRETURN); - } else if (targetMethodReturnType == LONG_TYPE) { - inj.visitInsn(LRETURN); - } else if (targetMethodReturnType == FLOAT_TYPE) { - inj.visitInsn(FRETURN); - } else if (targetMethodReturnType == DOUBLE_TYPE) { - inj.visitInsn(DRETURN); - } else if (targetMethodReturnType == VOID_TYPE) { - inj.visitInsn(RETURN); - } else { - inj.visitInsn(ARETURN); - } - } - - private void injectInvokeStatic(HookInjectorMethodVisitor inj, int returnLocalId, String name, String desc) { - for (int i = 0; i < hookMethodParameters.size(); i++) { - Type parameterType = hookMethodParameters.get(i); - int variableId = transmittableVariableIds.get(i); - if (inj.isStatic) { - // если попытка передачи this из статического метода, то передаем null - if (variableId == 0) { - inj.visitInsn(Opcodes.ACONST_NULL); - continue; - } - // иначе сдвигаем номер локальной переменной - if (variableId > 0) variableId--; - } - if (variableId == -1) variableId = returnLocalId; - injectLoad(inj, parameterType, variableId); - } - - inj.visitMethodInsn(INVOKESTATIC, getHookClassInternalName(), name, desc, false); - } - - public String getPatchedMethodName() { - return targetClassName + '#' + targetMethodName + targetMethodDescription; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("AsmHook: "); - - sb.append(targetClassName).append('#').append(targetMethodName); - sb.append(targetMethodDescription); - sb.append(" -> "); - sb.append(hooksClassName).append('#').append(hookMethodName); - sb.append(hookMethodDescription); - - sb.append(", ReturnCondition=").append(returnCondition); - sb.append(", ReturnValue=").append(returnValue); - if (returnValue == ReturnValue.PRIMITIVE_CONSTANT) sb.append(", Constant=").append(primitiveConstant); - sb.append(", InjectorFactory: ").append(injectorFactory.getClass().getName()); - sb.append(", CreateMethod = ").append(createMethod); - - return sb.toString(); - } - - @Override - public int compareTo(AsmHook o) { - if (injectorFactory.isPriorityInverted && o.injectorFactory.isPriorityInverted) { - return priority.ordinal() > o.priority.ordinal() ? -1 : 1; - } else if (!injectorFactory.isPriorityInverted && !o.injectorFactory.isPriorityInverted) { - return priority.ordinal() > o.priority.ordinal() ? 1 : -1; - } else { - return injectorFactory.isPriorityInverted ? 1 : -1; - } - } - - public static Builder newBuilder() { - return new AsmHook().new Builder(); - } - - public class Builder extends AsmHook { - - private Builder() { - - } - - /** - * --- ОБЯЗАТЕЛЬНО ВЫЗВАТЬ --- - * Определяет название класса, в который необходимо установить хук. - * - * @param className Название класса с указанием пакета, разделенное точками. - * Например: net.minecraft.world.World - */ - public Builder setTargetClass(String className) { - AsmHook.this.targetClassName = className; - return this; - } - - /** - * --- ОБЯЗАТЕЛЬНО ВЫЗВАТЬ --- - * Определяет название метода, в который необходимо вставить хук. - * Если нужно пропатчить конструктор, то в названии метода нужно указать . - * - * @param methodName Название метода. - * Например: getBlockId - */ - public Builder setTargetMethod(String methodName) { - AsmHook.this.targetMethodName = methodName; - return this; - } - - /** - * --- ОБЯЗАТЕЛЬНО ВЫЗВАТЬ, ЕСЛИ У ЦЕЛЕВОГО МЕТОДА ЕСТЬ ПАРАМЕТРЫ --- - * Добавляет один или несколько параметров к списку параметров целевого метода. - *

- * Эти параметры используются, чтобы составить описание целевого метода. - * Чтобы однозначно определить целевой метод, недостаточно только его названия - нужно ещё и описание. - *

- * Примеры использования: - * import static org.cifrazia.repack.gloomyfolken.hooklib.asm.TypeHelper.* - * //... - * addTargetMethodParameters(Type.INT_TYPE) - * Type worldType = getType("net.minecraft.world.World") - * Type playerType = getType("net.minecraft.entity.player.EntityPlayer") - * addTargetMethodParameters(worldType, playerType, playerType) - * - * @param parameterTypes Типы параметров целевого метода - * @see TypeHelper - */ - public Builder addTargetMethodParameters(Type... parameterTypes) { - for (Type type : parameterTypes) { - AsmHook.this.targetMethodParameters.add(type); - } - return this; - } - - /** - * Добавляет один или несколько параметров к списку параметров целевого метода. - * Обёртка над addTargetMethodParameters(Type... parameterTypes), которая сама строит типы из названия. - * - * @param parameterTypeNames Названия классов параметров целевого метода. - * Например: net.minecraft.world.World - */ - - public Builder addTargetMethodParameters(String... parameterTypeNames) { - Type[] types = new Type[parameterTypeNames.length]; - for (int i = 0; i < parameterTypeNames.length; i++) { - types[i] = TypeHelper.getType(parameterTypeNames[i]); - } - return addTargetMethodParameters(types); - } - - /** - * Изменяет тип, возвращаемый целевым методом. - * Вовращаемый тип используется, чтобы составить описание целевого метода. - * Чтобы однозначно определить целевой метод, недостаточно только его названия - нужно ещё и описание. - * По умолчанию хук применяется ко всем методам, подходящим по названию и списку параметров. - * - * @param returnType Тип, возвращаемый целевым методом - * @see TypeHelper - */ - public Builder setTargetMethodReturnType(Type returnType) { - AsmHook.this.targetMethodReturnType = returnType; - return this; - } - - /** - * Изменяет тип, возвращаемый целевым методом. - * Обёртка над setTargetMethodReturnType(Type returnType) - * - * @param returnType Название класса, экземпляр которого возвращает целевой метод - */ - public Builder setTargetMethodReturnType(String returnType) { - return setTargetMethodReturnType(TypeHelper.getType(returnType)); - } - - /** - * --- ОБЯЗАТЕЛЬНО ВЫЗВАТЬ, ЕСЛИ НУЖЕН ХУК-МЕТОД, А НЕ ПРОСТО return SOME_CONSTANT --- - * Определяет название класса, в котором находится хук-метод. - * - * @param className Название класса с указанием пакета, разделенное точками. - * Например: net.myname.mymod.asm.MyHooks - */ - public Builder setHookClass(String className) { - AsmHook.this.hooksClassName = className; - return this; - } - - /** - * --- ОБЯЗАТЕЛЬНО ВЫЗВАТЬ, ЕСЛИ НУЖЕН ХУК-МЕТОД, А НЕ ПРОСТО return SOME_CONSTANT --- - * Определяет название хук-метода. - * ХУК-МЕТОД ДОЛЖЕН БЫТЬ СТАТИЧЕСКИМ, А ПРОВЕРКИ НА ЭТО НЕТ. Будьте внимательны. - * - * @param methodName Название хук-метода. - * Например: myFirstHook - */ - public Builder setHookMethod(String methodName) { - AsmHook.this.hookMethodName = methodName; - return this; - } - - /** - * --- ОБЯЗАТЕЛЬНО ВЫЗВАТЬ, ЕСЛИ У ХУК-МЕТОДА ЕСТЬ ПАРАМЕТРЫ --- - * Добавляет параметр в список параметров хук-метода. - * В байткоде не сохраняются названия параметров. Вместо этого приходится использовать их номера. - * Например, в классе EntityLivingBase есть метод attackEntityFrom(DamageSource damageSource, float damage). - * В нём будут использоваться такие номера параметров: - * 1 - damageSource - * 2 - damage - * ВАЖНЫЙ МОМЕНТ: LONG И DOUBLE "ЗАНИМАЮТ" ДВА НОМЕРА. - * Теоретически, кроме параметров в хук-метод можно передать и локальные переменные, но их - * номера сложнее посчитать. - * Например, в классе Entity есть метод setPosition(double x, double y, double z). - * В нём будут такие номера параметров: - * 1 - x - * 2 - пропущено - * 3 - y - * 4 - пропущено - * 5 - z - * 6 - пропущено - *

- * Код этого метода таков: - * //... - * float f = ...; - * float f1 = ...; - * //... - * В таком случае у f будет номер 7, а у f1 - 8. - *

- * Если целевой метод static, то не нужно начинать отсчет локальных переменных с нуля, номера - * будут смещены автоматически. - * - * @param parameterType Тип параметра хук-метода - * @param variableId ID значения, передаваемого в хук-метод - * @throws IllegalStateException если не задано название хук-метода или класса, который его содержит - */ - public Builder addHookMethodParameter(Type parameterType, int variableId) { - if (!AsmHook.this.hasHookMethod()) { - throw new IllegalStateException("Hook method is not specified, so can not append " + - "parameter to its parameters list."); - } - AsmHook.this.hookMethodParameters.add(parameterType); - AsmHook.this.transmittableVariableIds.add(variableId); - return this; - } - - /** - * Добавляет параметр в список параметров целевого метода. - * Обёртка над addHookMethodParameter(Type parameterType, int variableId) - * - * @param parameterTypeName Название типа параметра хук-метода. - * Например: net.minecraft.world.World - * @param variableId ID значения, передаваемого в хук-метод - */ - public Builder addHookMethodParameter(String parameterTypeName, int variableId) { - return addHookMethodParameter(TypeHelper.getType(parameterTypeName), variableId); - } - - /** - * Добавляет в список параметров хук-метода целевой класс и передает хук-методу this. - * Если целевой метод static, то будет передано null. - * - * @throws IllegalStateException если не задан хук-метод - */ - public Builder addThisToHookMethodParameters() { - if (!AsmHook.this.hasHookMethod()) { - throw new IllegalStateException("Hook method is not specified, so can not append " + - "parameter to its parameters list."); - } - AsmHook.this.hookMethodParameters.add(TypeHelper.getType(targetClassName)); - AsmHook.this.transmittableVariableIds.add(0); - return this; - } - - /** - * Добавляет в список параметров хук-метода тип, возвращаемый целевым методом и - * передает хук-методу значение, которое вернёт return. - * Более формально, при вызове хук-метода указывает в качестве этого параметра верхнее значение в стеке. - * На практике основное применение - - * Например, есть такой код метода: - * int foo = bar(); - * return foo; - * Или такой: - * return bar() - *

- * В обоих случаях хук-методу можно передать возвращаемое значение перед вызовом return. - * - * @throws IllegalStateException если целевой метод возвращает void - * @throws IllegalStateException если не задан хук-метод - */ - public Builder addReturnValueToHookMethodParameters() { - if (!AsmHook.this.hasHookMethod()) { - throw new IllegalStateException("Hook method is not specified, so can not append " + - "parameter to its parameters list."); - } - if (AsmHook.this.targetMethodReturnType == Type.VOID_TYPE) { - throw new IllegalStateException("Target method's return type is void, it does not make sense to " + - "transmit its return value to hook method."); - } - AsmHook.this.hookMethodParameters.add(AsmHook.this.targetMethodReturnType); - AsmHook.this.transmittableVariableIds.add(-1); - AsmHook.this.hasReturnValueParameter = true; - return this; - } - - /** - * Задает условие, при котором после вызова хук-метода вызывается return. - * По умолчанию return не вызывается вообще. - * Кроме того, этот метод изменяет тип возвращаемого значения хук-метода: - * NEVER -> void - * ALWAYS -> void - * ON_TRUE -> boolean - * ON_NULL -> Object - * ON_NOT_NULL -> Object - * - * @param condition Условие выхода после вызова хук-метода - * @throws IllegalArgumentException если condition == ON_TRUE, ON_NULL или ON_NOT_NULL, но не задан хук-метод. - * @see ReturnCondition - */ - public Builder setReturnCondition(ReturnCondition condition) { - if (condition.requiresCondition && AsmHook.this.hookMethodName == null) { - throw new IllegalArgumentException("Hook method is not specified, so can not use return " + - "condition that depends on hook method."); - } - - AsmHook.this.returnCondition = condition; - Type returnType; - switch (condition) { - case NEVER: - case ALWAYS: - returnType = VOID_TYPE; - break; - case ON_TRUE: - returnType = BOOLEAN_TYPE; - break; - default: - returnType = getType(Object.class); - break; - } - AsmHook.this.hookMethodReturnType = returnType; - return this; - } - - /** - * --- ОБЯЗАТЕЛЬНО ВЫЗВАТЬ, ЕСЛИ ЦЕЛЕВОЙ МЕТОД ВОЗВРАЩАЕТ НЕ void, И ВЫЗВАН setReturnCondition --- - * Задает значение, которое возвращается при вызове return после вызова хук-метода. - * Следует вызывать после setReturnCondition. - * По умолчанию возвращается void. - * Кроме того, если value == ReturnValue.HOOK_RETURN_VALUE, то этот метод изменяет тип возвращаемого - * значения хук-метода на тип, указанный в setTargetMethodReturnType() - * - * @param value возвращаемое значение - * @throws IllegalStateException если returnCondition == NEVER (т. е. если setReturnCondition() не вызывался). - * Нет смысла указывать возвращаемое значение, если return не вызывается. - * @throws IllegalArgumentException если value == ReturnValue.HOOK_RETURN_VALUE, а тип возвращаемого значения - * целевого метода указан как void (или setTargetMethodReturnType ещё не вызывался). - * Нет смысла использовать значение, которое вернул хук-метод, если метод возвращает void. - */ - public Builder setReturnValue(ReturnValue value) { - if (AsmHook.this.returnCondition == ReturnCondition.NEVER) { - throw new IllegalStateException("Current return condition is ReturnCondition.NEVER, so it does not " + - "make sense to specify the return value."); - } - Type returnType = AsmHook.this.targetMethodReturnType; - if (value != ReturnValue.VOID && returnType == VOID_TYPE) { - throw new IllegalArgumentException("Target method return value is void, so it does not make sense to " + - "return anything else."); - } - if (value == ReturnValue.VOID && returnType != VOID_TYPE) { - throw new IllegalArgumentException("Target method return value is not void, so it is impossible " + - "to return VOID."); - } - if (value == ReturnValue.PRIMITIVE_CONSTANT && returnType != null && !isPrimitive(returnType)) { - throw new IllegalArgumentException("Target method return value is not a primitive, so it is " + - "impossible to return PRIVITIVE_CONSTANT."); - } - if (value == ReturnValue.NULL && returnType != null && isPrimitive(returnType)) { - throw new IllegalArgumentException("Target method return value is a primitive, so it is impossible " + - "to return NULL."); - } - if (value == ReturnValue.HOOK_RETURN_VALUE && !hasHookMethod()) { - throw new IllegalArgumentException("Hook method is not specified, so can not use return " + - "value that depends on hook method."); - } - - AsmHook.this.returnValue = value; - if (value == ReturnValue.HOOK_RETURN_VALUE) { - AsmHook.this.hookMethodReturnType = AsmHook.this.targetMethodReturnType; - } - return this; - } - - /** - * Возвращает тип возвращаемого значения хук-метода, если кому-то сложно "вычислить" его самостоятельно. - * - * @return тип возвращаемого значения хук-метода - */ - public Type getHookMethodReturnType() { - return hookMethodReturnType; - } - - /** - * Напрямую указывает тип, возвращаемый хук-методом. - * - * @param type - */ - protected void setHookMethodReturnType(Type type) { - AsmHook.this.hookMethodReturnType = type; - } - - private boolean isPrimitive(Type type) { - return type.getSort() > 0 && type.getSort() < 9; - } - - /** - * --- ОБЯЗАТЕЛЬНО ВЫЗВАТЬ, ЕСЛИ ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ УСТАНОВЛЕНО НА PRIMITIVE_CONSTANT --- - * Следует вызывать после setReturnValue(ReturnValue.PRIMITIVE_CONSTANT) - * Задает константу, которая будет возвращена при вызове return. - * Класс заданного объекта должен соответствовать примитивному типу. - * Например, если целевой метод возвращает int, то в этот метод должен быть передан объект класса Integer. - * - * @param constant Объект, класс которого соответствует примитиву, который следует возвращать. - * @throws IllegalStateException если возвращаемое значение не установлено на PRIMITIVE_CONSTANT - * @throws IllegalArgumentException если класс объекта constant не является обёрткой - * для примитивного типа, который возвращает целевой метод. - */ - public Builder setPrimitiveConstant(Object constant) { - if (AsmHook.this.returnValue != ReturnValue.PRIMITIVE_CONSTANT) { - throw new IllegalStateException("Return value is not PRIMITIVE_CONSTANT, so it does not make sence" + - "to specify that constant."); - } - Type returnType = AsmHook.this.targetMethodReturnType; - if (returnType == BOOLEAN_TYPE && !(constant instanceof Boolean) || - returnType == CHAR_TYPE && !(constant instanceof Character) || - returnType == BYTE_TYPE && !(constant instanceof Byte) || - returnType == SHORT_TYPE && !(constant instanceof Short) || - returnType == INT_TYPE && !(constant instanceof Integer) || - returnType == LONG_TYPE && !(constant instanceof Long) || - returnType == FLOAT_TYPE && !(constant instanceof Float) || - returnType == DOUBLE_TYPE && !(constant instanceof Double)) { - throw new IllegalArgumentException("Given object class does not math target method return type."); - } - - AsmHook.this.primitiveConstant = constant; - return this; - } - - /** - * --- ОБЯЗАТЕЛЬНО ВЫЗВАТЬ, ЕСЛИ ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ УСТАНОВЛЕНО НА ANOTHER_METHOD_RETURN_VALUE --- - * Следует вызывать после setReturnValue(ReturnValue.ANOTHER_METHOD_RETURN_VALUE) - * Задает метод, результат вызова которого будет возвращён при вызове return. - * - * @param methodName название метода, результат вызова которого следует возвращать - * @throws IllegalStateException если возвращаемое значение не установлено на ANOTHER_METHOD_RETURN_VALUE - */ - public Builder setReturnMethod(String methodName) { - if (AsmHook.this.returnValue != ReturnValue.ANOTHER_METHOD_RETURN_VALUE) { - throw new IllegalStateException("Return value is not ANOTHER_METHOD_RETURN_VALUE, " + - "so it does not make sence to specify that method."); - } - - AsmHook.this.returnMethodName = methodName; - return this; - } - - /** - * Задает фабрику, которая создаст инжектор для этого хука. - * Если говорить более человеческим языком, то этот метод определяет, где будет вставлен хук: - * в начале метода, в конце или где-то ещё. - * Если не создавать своих инжекторов, то можно использовать две фабрики: - * AsmHook.ON_ENTER_FACTORY (вставляет хук на входе в метод, используется по умолчанию) - * AsmHook.ON_EXIT_FACTORY (вставляет хук на выходе из метода) - * - * @param factory Фабрика, создающая инжектор для этого хука - */ - public Builder setInjectorFactory(HookInjectorFactory factory) { - AsmHook.this.injectorFactory = factory; - return this; - } - - /** - * Задает приоритет хука. - * Хуки с большим приоритетом вызаваются раньше. - */ - public Builder setPriority(HookPriority priority) { - AsmHook.this.priority = priority; - return this; - } - - /** - * Позволяет не только вставлять хуки в существующие методы, но и добавлять новые. Это может понадобиться, - * когда нужно переопределить метод суперкласса. Если супер-метод найден, то тело генерируемого метода - * представляет собой вызов супер-метода. Иначе это просто пустой метод или return false/0/null в зависимости - * от возвращаемого типа. - */ - public Builder setCreateMethod(boolean createMethod) { - AsmHook.this.createMethod = createMethod; - return this; - } - - /** - * Позволяет объявить хук "обязательным" для запуска игры. В случае неудачи во время вставки такого хука - * будет не просто выведено сообщение в лог, а крашнется игра. - */ - public Builder setMandatory(boolean isMandatory) { - AsmHook.this.isMandatory = isMandatory; - return this; - } - - private String getMethodDesc(Type returnType, List paramTypes) { - Type[] paramTypesArray = paramTypes.toArray(new Type[0]); - if (returnType == null) { - String voidDesc = Type.getMethodDescriptor(Type.VOID_TYPE, paramTypesArray); - return voidDesc.substring(0, voidDesc.length() - 1); - } else { - return Type.getMethodDescriptor(returnType, paramTypesArray); - } - } - - /** - * Создает хук по заданным параметрам. - * - * @return полученный хук - * @throws IllegalStateException если не был вызван какой-либо из обязательных методов - */ - public AsmHook build() { - AsmHook hook = AsmHook.this; - - if (hook.createMethod && hook.targetMethodReturnType == null) { - hook.targetMethodReturnType = hook.hookMethodReturnType; - } - hook.targetMethodDescription = getMethodDesc(hook.targetMethodReturnType, hook.targetMethodParameters); - - if (hook.hasHookMethod()) { - hook.hookMethodDescription = Type.getMethodDescriptor(hook.hookMethodReturnType, - hook.hookMethodParameters.toArray(new Type[0])); - } - if (hook.returnValue == ReturnValue.ANOTHER_METHOD_RETURN_VALUE) { - hook.returnMethodDescription = getMethodDesc(hook.targetMethodReturnType, hook.hookMethodParameters); - } - - try { - hook = (AsmHook) AsmHook.this.clone(); - } catch (CloneNotSupportedException impossible) { - } - - if (hook.targetClassName == null) { - throw new IllegalStateException("Target class name is not specified. " + - "Call setTargetClassName() before build()."); - } - - if (hook.targetMethodName == null) { - throw new IllegalStateException("Target method name is not specified. " + - "Call setTargetMethodName() before build()."); - } - - if (hook.returnValue == ReturnValue.PRIMITIVE_CONSTANT && hook.primitiveConstant == null) { - throw new IllegalStateException("Return value is PRIMITIVE_CONSTANT, but the constant is not " + - "specified. Call setReturnValue() before build()."); - } - - if (hook.returnValue == ReturnValue.ANOTHER_METHOD_RETURN_VALUE && hook.returnMethodName == null) { - throw new IllegalStateException("Return value is ANOTHER_METHOD_RETURN_VALUE, but the method is not " + - "specified. Call setReturnMethod() before build()."); - } - - if (!(hook.injectorFactory instanceof HookInjectorFactory.MethodExit) && hook.hasReturnValueParameter) { - throw new IllegalStateException("Can not pass return value to hook method " + - "because hook location is not return insn."); - } - - return hook; - } - - } - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ClassMetadataReader.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ClassMetadataReader.java deleted file mode 100644 index db885e14f..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ClassMetadataReader.java +++ /dev/null @@ -1,201 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.apache.commons.io.IOUtils; -import org.objectweb.asm.*; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; - -/** - * Позволяет при помощи велосипеда из костылей искать методы внутри незагруженных классов - * и общие суперклассы для чего угодно. Работает через поиск class-файлов в classpath, и, в случае провала - - * ищет через рефлексию. Для работы с майнкрафтом используется сабкласс под названием DeobfuscationMetadataReader, - * - */ -public class ClassMetadataReader { - private static Method m; - - static { - try { - m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); - m.setAccessible(true); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - } - - public byte[] getClassData(String className) throws IOException { - String classResourceName = '/' + className.replace('.', '/') + ".class"; - return IOUtils.toByteArray(ClassMetadataReader.class.getResourceAsStream(classResourceName)); - } - - public void acceptVisitor(byte[] classData, ClassVisitor visitor) { - new ClassReader(classData).accept(visitor, 0); - } - - public void acceptVisitor(String className, ClassVisitor visitor) throws IOException { - acceptVisitor(getClassData(className), visitor); - } - - public MethodReference findVirtualMethod(String owner, String name, String desc) { - ArrayList superClasses = getSuperClasses(owner); - for (int i = superClasses.size() - 1; i > 0; i--) { // чекать текущий класс смысла нет - String className = superClasses.get(i); - MethodReference methodReference = getMethodReference(className, name, desc); - if (methodReference != null) { - System.out.println("found virtual method: " + methodReference); - return methodReference; - } - } - return null; - } - - private MethodReference getMethodReference(String type, String methodName, String desc) { - try { - return getMethodReferenceASM(type, methodName, desc); - } catch (Exception e) { - return getMethodReferenceReflect(type, methodName, desc); - } - } - - protected MethodReference getMethodReferenceASM(String type, String methodName, String desc) throws IOException { - FindMethodClassVisitor cv = new FindMethodClassVisitor(methodName, desc); - acceptVisitor(type, cv); - if (cv.found) { - return new MethodReference(type, cv.targetName, cv.targetDesc); - } - return null; - } - - protected MethodReference getMethodReferenceReflect(String type, String methodName, String desc) { - Class loadedClass = getLoadedClass(type); - if (loadedClass != null) { - for (Method m : loadedClass.getDeclaredMethods()) { - if (checkSameMethod(methodName, desc, m.getName(), Type.getMethodDescriptor(m))) { - return new MethodReference(type, m.getName(), Type.getMethodDescriptor(m)); - } - } - } - return null; - } - - protected boolean checkSameMethod(String sourceName, String sourceDesc, String targetName, String targetDesc) { - return sourceName.equals(targetName) && sourceDesc.equals(targetDesc); - } - - /** - * Возвращает суперклассы в порядке возрастающей конкретности (начиная с java/lang/Object - * и заканчивая данным типом) - */ - public ArrayList getSuperClasses(String type) { - ArrayList superclasses = new ArrayList(1); - superclasses.add(type); - while ((type = getSuperClass(type)) != null) { - superclasses.add(type); - } - Collections.reverse(superclasses); - return superclasses; - } - - private Class getLoadedClass(String type) { - if (m != null) { - try { - ClassLoader classLoader = ClassMetadataReader.class.getClassLoader(); - return (Class) m.invoke(classLoader, type.replace('/', '.')); - } catch (Exception e) { - e.printStackTrace(); - } - } - return null; - } - - public String getSuperClass(String type) { - try { - return getSuperClassASM(type); - } catch (Exception e) { - return getSuperClassReflect(type); - } - } - - protected String getSuperClassASM(String type) throws IOException { - CheckSuperClassVisitor cv = new CheckSuperClassVisitor(); - acceptVisitor(type, cv); - return cv.superClassName; - } - - protected String getSuperClassReflect(String type) { - Class loadedClass = getLoadedClass(type); - if (loadedClass != null) { - if (loadedClass.getSuperclass() == null) return null; - return loadedClass.getSuperclass().getName().replace('.', '/'); - } - return "java/lang/Object"; - } - - private class CheckSuperClassVisitor extends ClassVisitor { - - String superClassName; - - public CheckSuperClassVisitor() { - super(Opcodes.ASM5); - } - - @Override - public void visit(int version, int access, String name, String signature, - String superName, String[] interfaces) { - this.superClassName = superName; - } - } - - protected class FindMethodClassVisitor extends ClassVisitor { - - public String targetName; - public String targetDesc; - public boolean found; - - public FindMethodClassVisitor(String name, String desc) { - super(Opcodes.ASM5); - this.targetName = name; - this.targetDesc = desc; - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - System.out.println("visiting " + name + "#" + desc); - if ((access & Opcodes.ACC_PRIVATE) == 0 && checkSameMethod(name, desc, targetName, targetDesc)) { - found = true; - targetName = name; - targetDesc = desc; - } - return null; - } - } - - public static class MethodReference { - - public final String owner; - public final String name; - public final String desc; - - public MethodReference(String owner, String name, String desc) { - this.owner = owner; - this.name = name; - this.desc = desc; - } - - public Type getType() { - return Type.getMethodType(desc); - } - - @Override public String toString() { - return "MethodReference{" + - "owner='" + owner + '\'' + - ", name='" + name + '\'' + - ", desc='" + desc + '\'' + - '}'; - } - } - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/Hook.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/Hook.java deleted file mode 100644 index 4517ca3f2..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/Hook.java +++ /dev/null @@ -1,139 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - -/** - * Чтобы сделать метод хуком, нужно повесить над ним эту аннотацию и зарегистрировать класс с хуком. - *

- * Целевой класс определяется первым параметром хук-метода. Если целевой метод static, то туда прилетает null, - * иначе - this. - *

- * Название целевого метода по умолчанию такое же, как название хук-метода, но его можно переопределить через - * targetMethod. - *

- * Список параметров целевого метода определяется списком параметров хук-метода. Нужно добавить все те же параметры - * в том же порядке. - *

- * Возвращаемый тип целевого метода по умолчанию не указывается вообще. Предполагается, что методов с одинаковым - * названием и списком параметров нет. Если всё же нужно указать, то это можно сделать через returnType. - */ -@Target(ElementType.METHOD) -public @interface Hook { - - /** - * Задает условие, по которому после вызова хука будет вызван return. - * Если целевой метод возвращает не void, то по умолчанию будет возвращено то, что вернул хук-метод. - * Это можно переопредилить несколькими элементами аннотации: - * returnAnotherMethod, returnNull и %type%ReturnConstant. - */ - ReturnCondition returnCondition() default ReturnCondition.NEVER; - - /** - * Задает приоритет хука. - * Хуки с большим приоритетом вызаваются раньше. - */ - HookPriority priority() default HookPriority.NORMAL; - - /** - * Задает название целевого метода. - * По умолчанию используется название хук-метода. - * Эта опция полезна, когда нужно вставить хук в конструктор или инициализацию класса. - * Для конструктора targetMethod должен быть "", для инициализации класса - "" - */ - String targetMethod() default ""; - - /** - * Задает тип, возвращаемый целевым методом. - * С точки зрения JVM могут быть методы, которые отличаются только возращаемым типом. - * На практике компиляторы таких методов не генерируют, но в некоторых случаях они - * могут встретиться (например, это можно сделать при обфускации через ProGuard) - * Если возвращаемый тип не указан, то хук применяется к первому методу, подходящему - * по названию и списку параметров. - * - * Основной предполагаемый способ использования этого параметра - вместе с createMethod = true. - * В этом случае созданный метод будет по умолчанию иметь тот же возвращаемый тип, что и хук-метод, - * а с помощью этого параметра это можно изменить. - * - * Указывать нужно полное название класса: java.lang.String, void, int и т.д. - */ - String returnType() default ""; - - /** - * Позволяет не только вставлять хуки в существующие методы, но и добавлять новые. Это может понадобиться, - * когда нужно переопределить метод суперкласса. Если супер-метод найден, то тело генерируемого метода - * представляет собой вызов супер-метода. Иначе это просто пустой метод или return false/0/null в зависимости - * от возвращаемого типа. - */ - boolean createMethod() default false; - - - /** - * Позволяет объявить хук "обязательным" для запуска игры. В случае неудачи во время вставки такого хука - * будет не просто выведено сообщение в лог, а крашнется игра. - */ - // CUSTOM: in original version the default was false - boolean isMandatory() default true; - - /** - * По умолчанию хук вставляется в начало целевого метода. - * Если указать здесь true, то он будет вставлен в конце и перед каждым вызовом return. - */ - boolean injectOnExit() default false; - - /** - * По умолчанию хук вставляется в начало целевого метода. - * Если указать здесь true, то он будет вставлен в начале указанной строки. - * Использовать не рекомендуется, потому что: - * 1) Вставить можно только на строку с инструкцией - * 2) Может ВНЕЗАПНО сломаться (например, от того, что какой-нибудь оптифайн подменит класс целиком) - */ - @Deprecated int injectOnLine() default -1; - - /** - * Если указано это название, то при вызове return в целевом методе будет сначала вызван этот метод. - * Он должен находиться в том же классе и иметь тот же список параметров, что и хук-метод. - * В итоге будет возвращено значение, которое вернёт этот метод. - */ - String returnAnotherMethod() default ""; - - /** - * Если true, то при вызове return в целевом методе будет возвращено null - */ - boolean returnNull() default false; - - /** - * Если определена одна из этих констант, то она будет возвращена при вызове return в целевом методе - */ - - boolean booleanReturnConstant() default false; - - byte byteReturnConstant() default 0; - - short shortReturnConstant() default 0; - - int intReturnConstant() default 0; - - long longReturnConstant() default 0L; - - float floatReturnConstant() default 0.0F; - - double doubleReturnConstant() default 0.0D; - - char charReturnConstant() default 0; - - String stringReturnConstant() default ""; - - @Target(ElementType.PARAMETER) - @interface LocalVariable { - int value(); - } - - /** - * Перехватывает значение, которое изначально шло в return, и передает его хук-методу. - * Говоря более формально, передает последнее значение в стаке. - * Можно использовать только когда injectOnExit() == true и целевой метод возвращает не void. - */ - @Target(ElementType.PARAMETER) - @interface ReturnValue {} -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookClassTransformer.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookClassTransformer.java deleted file mode 100644 index 59cb2abc0..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookClassTransformer.java +++ /dev/null @@ -1,108 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -public class HookClassTransformer { - - public HookLogger logger = new HookLogger.SystemOutLogger(); - protected HashMap> hooksMap = new HashMap>(); - private HookContainerParser containerParser = new HookContainerParser(this); - protected ClassMetadataReader classMetadataReader = new ClassMetadataReader(); - - public void registerHook(AsmHook hook) { - if (hooksMap.containsKey(hook.getTargetClassName())) { - hooksMap.get(hook.getTargetClassName()).add(hook); - } else { - List list = new ArrayList(2); - list.add(hook); - hooksMap.put(hook.getTargetClassName(), list); - } - } - - public void registerHookContainer(String className) { - containerParser.parseHooks(className); - } - - public void registerHookContainer(byte[] classData) { - containerParser.parseHooks(classData); - } - - public byte[] transform(String className, byte[] bytecode) { - List hooks = hooksMap.get(className); - - if (hooks != null) { - Collections.sort(hooks); - logger.debug("Injecting hooks into class " + className); - try { - /* - Начиная с седьмой версии джавы, сильно изменился процесс верификации байткода. - Ради этого приходится включать автоматическую генерацию stack map frame'ов. - На более старых версиях байткода это лишняя трата времени. - Подробнее здесь: http://stackoverflow.com/questions/25109942 - */ - int majorVersion = ((bytecode[6] & 0xFF) << 8) | (bytecode[7] & 0xFF); - boolean java7 = majorVersion > 50; - - - ClassReader cr = new ClassReader(bytecode); - ClassWriter cw = createClassWriter(java7 ? ClassWriter.COMPUTE_FRAMES : ClassWriter.COMPUTE_MAXS); - HookInjectorClassVisitor hooksWriter = createInjectorClassVisitor(cw, hooks); - cr.accept(hooksWriter, java7 ? ClassReader.SKIP_FRAMES : ClassReader.EXPAND_FRAMES); - bytecode = cw.toByteArray(); - for (AsmHook hook : hooksWriter.injectedHooks) { - logger.debug("Patching method " + hook.getPatchedMethodName()); - } - hooks.removeAll(hooksWriter.injectedHooks); - } catch (Exception e) { - logger.severe("A problem has occurred during transformation of class " + className + "."); - logger.severe("Attached hooks:"); - for (AsmHook hook : hooks) { - logger.severe(hook.toString()); - } - logger.severe("Stack trace:", e); - } - - for (AsmHook notInjected : hooks) { - if (notInjected.isMandatory()) { - throw new RuntimeException("Can not find target method of mandatory hook " + notInjected); - } else { - logger.warning("Can not find target method of hook " + notInjected); - } - } - } - return bytecode; - } - - /** - * Создает ClassVisitor для списка хуков. - * Метод можно переопределить, если в ClassVisitor'e нужна своя логика для проверки, - * является ли метод целевым (isTargetMethod()) - * - * @param cw ClassWriter, который должен стоять в цепочке после этого ClassVisitor'a - * @param hooks Список хуков, вставляемых в класс - * @return ClassVisitor, добавляющий хуки - */ - protected HookInjectorClassVisitor createInjectorClassVisitor(ClassWriter cw, List hooks) { - return new HookInjectorClassVisitor(this, cw, hooks); - } - - /** - * Создает ClassWriter для сохранения трансформированного класса. - * Метод можно переопределить, если в ClassWriter'e нужна своя реализация метода getCommonSuperClass(). - * Стандартная реализация работает для уже загруженных классов и для классов, .class файлы которых есть - * в classpath, но они ещё не загружены. Во втором случае происходит загрузка (но не инициализация) классов. - * Если загрузка классов является проблемой, то можно воспользоваться SafeClassWriter. - * - * @param flags Список флагов, которые нужно передать в конструктор ClassWriter'a - * @return ClassWriter, сохраняющий трансформированный класс - */ - protected ClassWriter createClassWriter(int flags) { - return new SafeClassWriter(classMetadataReader, flags); - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookContainerParser.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookContainerParser.java deleted file mode 100644 index df83cb9ad..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookContainerParser.java +++ /dev/null @@ -1,266 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.*; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map.Entry; - -public class HookContainerParser { - - private HookClassTransformer transformer; - private String currentClassName; - private String currentMethodName; - private String currentMethodDesc; - private boolean currentMethodPublicStatic; - - /* - Ключ - название значения аннотации - */ - private HashMap annotationValues; - - /* - Ключ - номер параметра, значение - номер локальной переменной для перехвата - или -1 для перехвата значения наверху стека. - */ - private HashMap parameterAnnotations = new HashMap(); - - private boolean inHookAnnotation; - - private static final String HOOK_DESC = Type.getDescriptor(Hook.class); - private static final String LOCAL_DESC = Type.getDescriptor(Hook.LocalVariable.class); - private static final String RETURN_DESC = Type.getDescriptor(ReturnValue.class); - - public HookContainerParser(HookClassTransformer transformer) { - this.transformer = transformer; - } - - protected void parseHooks(String className) { - transformer.logger.debug("Parsing hooks container " + className); - try { - transformer.classMetadataReader.acceptVisitor(className, new HookClassVisitor()); - } catch (IOException e) { - transformer.logger.severe("Can not parse hooks container " + className, e); - } - } - - protected void parseHooks(byte[] classData) { - - } - - private void invalidHook(String message) { - transformer.logger.warning("Found invalid hook " + currentClassName + "#" + currentMethodName); - transformer.logger.warning(message); - } - - private void createHook() { - AsmHook.Builder builder = AsmHook.newBuilder(); - Type methodType = Type.getMethodType(currentMethodDesc); - Type[] argumentTypes = methodType.getArgumentTypes(); - - if (!currentMethodPublicStatic) { - invalidHook("Hook method must be public and static."); - return; - } - - if (argumentTypes.length < 1) { - invalidHook("Hook method has no parameters. First parameter of a " + - "hook method must belong the type of the target class."); - return; - } - - if (argumentTypes[0].getSort() != Type.OBJECT) { - invalidHook("First parameter of the hook method is not an object. First parameter of a " + - "hook method must belong the type of the target class."); - return; - } - - builder.setTargetClass(argumentTypes[0].getClassName()); - - if (annotationValues.containsKey("targetMethod")) { - builder.setTargetMethod((String) annotationValues.get("targetMethod")); - } else { - builder.setTargetMethod(currentMethodName); - } - - builder.setHookClass(currentClassName); - builder.setHookMethod(currentMethodName); - builder.addThisToHookMethodParameters(); - - boolean injectOnExit = Boolean.TRUE.equals(annotationValues.get("injectOnExit")); - - int currentParameterId = 1; - for (int i = 1; i < argumentTypes.length; i++) { - Type argType = argumentTypes[i]; - if (parameterAnnotations.containsKey(i)) { - int localId = parameterAnnotations.get(i); - if (localId == -1) { - builder.setTargetMethodReturnType(argType); - builder.addReturnValueToHookMethodParameters(); - } else { - builder.addHookMethodParameter(argType, localId); - } - } else { - builder.addTargetMethodParameters(argType); - builder.addHookMethodParameter(argType, currentParameterId); - currentParameterId += argType == Type.LONG_TYPE || argType == Type.DOUBLE_TYPE ? 2 : 1; - } - } - - if (injectOnExit) builder.setInjectorFactory(AsmHook.ON_EXIT_FACTORY); - - if (annotationValues.containsKey("injectOnLine")) { - int line = (Integer) annotationValues.get("injectOnLine"); - builder.setInjectorFactory(new HookInjectorFactory.LineNumber(line)); - } - - if (annotationValues.containsKey("returnType")) { - builder.setTargetMethodReturnType((String) annotationValues.get("returnType")); - } - - ReturnCondition returnCondition = ReturnCondition.NEVER; - if (annotationValues.containsKey("returnCondition")) { - returnCondition = ReturnCondition.valueOf((String) annotationValues.get("returnCondition")); - builder.setReturnCondition(returnCondition); - } - - if (returnCondition != ReturnCondition.NEVER) { - Object primitiveConstant = getPrimitiveConstant(); - if (primitiveConstant != null) { - builder.setReturnValue(ReturnValue.PRIMITIVE_CONSTANT); - builder.setPrimitiveConstant(primitiveConstant); - } else if (Boolean.TRUE.equals(annotationValues.get("returnNull"))) { - builder.setReturnValue(ReturnValue.NULL); - } else if (annotationValues.containsKey("returnAnotherMethod")) { - builder.setReturnValue(ReturnValue.ANOTHER_METHOD_RETURN_VALUE); - builder.setReturnMethod((String) annotationValues.get("returnAnotherMethod")); - } else if (methodType.getReturnType() != Type.VOID_TYPE) { - builder.setReturnValue(ReturnValue.HOOK_RETURN_VALUE); - } - } - - // setReturnCondition и setReturnValue сетают тип хук-метода, поэтому сетнуть его вручную можно только теперь - builder.setHookMethodReturnType(methodType.getReturnType()); - - if (returnCondition == ReturnCondition.ON_TRUE && methodType.getReturnType() != Type.BOOLEAN_TYPE) { - invalidHook("Hook method must return boolean if returnCodition is ON_TRUE."); - return; - } - if ((returnCondition == ReturnCondition.ON_NULL || returnCondition == ReturnCondition.ON_NOT_NULL) && - methodType.getReturnType().getSort() != Type.OBJECT && - methodType.getReturnType().getSort() != Type.ARRAY) { - invalidHook("Hook method must return object if returnCodition is ON_NULL or ON_NOT_NULL."); - return; - } - - if (annotationValues.containsKey("priority")) { - builder.setPriority(HookPriority.valueOf((String) annotationValues.get("priority"))); - } - - if (annotationValues.containsKey("createMethod")) { - builder.setCreateMethod(Boolean.TRUE.equals(annotationValues.get("createMethod"))); - } - if (annotationValues.containsKey("isMandatory")) { - builder.setMandatory(Boolean.TRUE.equals(annotationValues.get("isMandatory"))); - } - - transformer.registerHook(builder.build()); - } - - private Object getPrimitiveConstant() { - for (Entry entry : annotationValues.entrySet()) { - if (entry.getKey().endsWith("Constant")) { - return entry.getValue(); - } - } - return null; - } - - - private class HookClassVisitor extends ClassVisitor { - public HookClassVisitor() { - super(Opcodes.ASM5); - } - - @Override - public void visit(int version, int access, String name, String signature, - String superName, String[] interfaces) { - currentClassName = name.replace('/', '.'); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - currentMethodName = name; - currentMethodDesc = desc; - currentMethodPublicStatic = (access & Opcodes.ACC_PUBLIC) != 0 && (access & Opcodes.ACC_STATIC) != 0; - return new HookMethodVisitor(); - } - } - - private class HookMethodVisitor extends MethodVisitor { - - public HookMethodVisitor() { - super(Opcodes.ASM5); - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (HOOK_DESC.equals(desc)) { - annotationValues = new HashMap(); - inHookAnnotation = true; - } - return new HookAnnotationVisitor(); - } - - @Override - public AnnotationVisitor visitParameterAnnotation(final int parameter, String desc, boolean visible) { - if (RETURN_DESC.equals(desc)) { - parameterAnnotations.put(parameter, -1); - } - if (LOCAL_DESC.equals(desc)) { - return new AnnotationVisitor(Opcodes.ASM5) { - @Override - public void visit(String name, Object value) { - parameterAnnotations.put(parameter, (Integer) value); - } - }; - } - return null; - } - - @Override - public void visitEnd() { - if (annotationValues != null) { - createHook(); - } - parameterAnnotations.clear(); - currentMethodName = currentMethodDesc = null; - currentMethodPublicStatic = false; - annotationValues = null; - } - } - - private class HookAnnotationVisitor extends AnnotationVisitor { - - public HookAnnotationVisitor() { - super(Opcodes.ASM5); - } - - @Override - public void visit(String name, Object value) { - if (inHookAnnotation) { - annotationValues.put(name, value); - } - } - - @Override - public void visitEnum(String name, String desc, String value) { - visit(name, value); - } - - @Override - public void visitEnd() { - inHookAnnotation = false; - } - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookInjectorClassVisitor.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookInjectorClassVisitor.java deleted file mode 100644 index 6b1702e38..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookInjectorClassVisitor.java +++ /dev/null @@ -1,59 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -import java.util.ArrayList; -import java.util.List; - -public class HookInjectorClassVisitor extends ClassVisitor { - - List hooks; - List injectedHooks = new ArrayList(1); - boolean visitingHook; - HookClassTransformer transformer; - - String superName; - - public HookInjectorClassVisitor(HookClassTransformer transformer, ClassWriter cv, List hooks) { - super(Opcodes.ASM5, cv); - this.hooks = hooks; - this.transformer = transformer; - } - - @Override public void visit(int version, int access, String name, - String signature, String superName, String[] interfaces) { - this.superName = superName; - super.visit(version, access, name, signature, superName, interfaces); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, - String signature, String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); - for (AsmHook hook : hooks) { - if (isTargetMethod(hook, name, desc) && !injectedHooks.contains(hook)) { - // добавляет MethodVisitor в цепочку - mv = hook.getInjectorFactory().createHookInjector(mv, access, name, desc, hook, this); - injectedHooks.add(hook); - } - } - return mv; - } - - @Override - public void visitEnd() { - for (AsmHook hook : hooks) { - if (hook.getCreateMethod() && !injectedHooks.contains(hook)) { - hook.createMethod(this); - } - } - super.visitEnd(); - } - - protected boolean isTargetMethod(AsmHook hook, String name, String desc) { - return hook.isTargetMethod(name, desc); - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookInjectorFactory.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookInjectorFactory.java deleted file mode 100644 index dbdf26b87..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookInjectorFactory.java +++ /dev/null @@ -1,67 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.MethodVisitor; - -/** - * Фабрика, задающая тип инжектора хуков. Фактически, от выбора фабрики зависит то, в какие участки кода попадёт хук. - * "Из коробки" доступно два типа инжекторов: MethodEnter, который вставляет хук на входе в метод, - * и MethodExit, который вставляет хук на каждом выходе. - */ -public abstract class HookInjectorFactory { - - /** - * Метод AdviceAdapter#visitInsn() - штука странная. Там почему-то вызов следующего MethodVisitor'a - * производится после логики, а не до, как во всех остальных случаях. Поэтому для MethodExit приоритет - * хуков инвертируется. - */ - protected boolean isPriorityInverted = false; - - abstract HookInjectorMethodVisitor createHookInjector(MethodVisitor mv, int access, String name, String desc, - AsmHook hook, HookInjectorClassVisitor cv); - - - static class MethodEnter extends HookInjectorFactory { - - public static final MethodEnter INSTANCE = new MethodEnter(); - - private MethodEnter() {} - - @Override - public HookInjectorMethodVisitor createHookInjector(MethodVisitor mv, int access, String name, String desc, - AsmHook hook, HookInjectorClassVisitor cv) { - return new HookInjectorMethodVisitor.MethodEnter(mv, access, name, desc, hook, cv); - } - - } - - static class MethodExit extends HookInjectorFactory { - - public static final MethodExit INSTANCE = new MethodExit(); - - private MethodExit() { - isPriorityInverted = true; - } - - @Override - public HookInjectorMethodVisitor createHookInjector(MethodVisitor mv, int access, String name, String desc, - AsmHook hook, HookInjectorClassVisitor cv) { - return new HookInjectorMethodVisitor.MethodExit(mv, access, name, desc, hook, cv); - } - } - - static class LineNumber extends HookInjectorFactory { - - private int lineNumber; - - public LineNumber(int lineNumber) { - this.lineNumber = lineNumber; - } - - @Override - public HookInjectorMethodVisitor createHookInjector(MethodVisitor mv, int access, String name, String desc, - AsmHook hook, HookInjectorClassVisitor cv) { - return new HookInjectorMethodVisitor.LineNumber(mv, access, name, desc, hook, cv, lineNumber); - } - } - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookInjectorMethodVisitor.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookInjectorMethodVisitor.java deleted file mode 100644 index 869179363..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookInjectorMethodVisitor.java +++ /dev/null @@ -1,101 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.commons.AdviceAdapter; - -/** - * Класс, непосредственно вставляющий хук в метод. - * Чтобы указать конкретное место вставки хука, нужно создать класс extends HookInjector. - */ -public abstract class HookInjectorMethodVisitor extends AdviceAdapter { - - protected final AsmHook hook; - protected final HookInjectorClassVisitor cv; - public final String methodName; - public final Type methodType; - public final boolean isStatic; - - protected HookInjectorMethodVisitor(MethodVisitor mv, int access, String name, String desc, - AsmHook hook, HookInjectorClassVisitor cv) { - super(Opcodes.ASM5, mv, access, name, desc); - this.hook = hook; - this.cv = cv; - isStatic = (access & Opcodes.ACC_STATIC) != 0; - this.methodName = name; - this.methodType = Type.getMethodType(desc); - } - - /** - * Вставляет хук в байткод. - */ - protected final void visitHook() { - if (!cv.visitingHook) { - cv.visitingHook = true; - hook.inject(this); - cv.visitingHook = false; - } - } - - MethodVisitor getBasicVisitor() { - return mv; - } - - /** - * Вставляет хук в начале метода. - */ - public static class MethodEnter extends HookInjectorMethodVisitor { - - public MethodEnter(MethodVisitor mv, int access, String name, String desc, - AsmHook hook, HookInjectorClassVisitor cv) { - super(mv, access, name, desc, hook, cv); - } - - @Override - protected void onMethodEnter() { - visitHook(); - } - - } - - /** - * Вставляет хук на каждом выходе из метода, кроме выходов через throw. - */ - public static class MethodExit extends HookInjectorMethodVisitor { - - public MethodExit(MethodVisitor mv, int access, String name, String desc, - AsmHook hook, HookInjectorClassVisitor cv) { - super(mv, access, name, desc, hook, cv); - } - - @Override - protected void onMethodExit(int opcode) { - if (opcode != Opcodes.ATHROW) { - visitHook(); - } - } - } - - /** - * Вставляет хук по номеру строки. - */ - public static class LineNumber extends HookInjectorMethodVisitor { - - private int lineNumber; - - public LineNumber(MethodVisitor mv, int access, String name, String desc, - AsmHook hook, HookInjectorClassVisitor cv, int lineNumber) { - super(mv, access, name, desc, hook, cv); - this.lineNumber = lineNumber; - } - - @Override - public void visitLineNumber(int line, Label start) { - super.visitLineNumber(line, start); - if (this.lineNumber == line) visitHook(); - } - } - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookLogger.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookLogger.java deleted file mode 100644 index fb4ef45de..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookLogger.java +++ /dev/null @@ -1,69 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public interface HookLogger { - - void debug(String message); - - void warning(String message); - - void severe(String message); - - void severe(String message, Throwable cause); - - class SystemOutLogger implements HookLogger { - - @Override - public void debug(String message) { - System.out.println("[DEBUG] " + message); - } - - @Override - public void warning(String message) { - System.out.println("[WARNING] " + message); - } - - @Override - public void severe(String message) { - System.out.println("[SEVERE] " + message); - } - - @Override - public void severe(String message, Throwable cause) { - severe(message); - cause.printStackTrace(); - } - } - - class VanillaLogger implements HookLogger { - - private Logger logger; - - public VanillaLogger(Logger logger) { - this.logger = logger; - } - - @Override - public void debug(String message) { - logger.fine(message); - } - - @Override - public void warning(String message) { - logger.warning(message); - } - - @Override - public void severe(String message) { - logger.severe(message); - } - - @Override - public void severe(String message, Throwable cause) { - logger.log(Level.SEVERE, message, cause); - } - } - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookPriority.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookPriority.java deleted file mode 100644 index 2f56335c8..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/HookPriority.java +++ /dev/null @@ -1,11 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -public enum HookPriority { - - HIGHEST, // Вызывается первым - HIGH, - NORMAL, - LOW, - LOWEST // Вызывается последним - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ReadClassHelper.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ReadClassHelper.java deleted file mode 100644 index 4151dcf55..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ReadClassHelper.java +++ /dev/null @@ -1,28 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; - -import java.io.InputStream; - -public class ReadClassHelper { - - public static InputStream getClassData(String className) { - String classResourceName = '/' + className.replace('.', '/') + ".class"; - return ReadClassHelper.class.getResourceAsStream(classResourceName); - } - - public static void acceptVisitor(InputStream classData, ClassVisitor visitor) { - try { - ClassReader reader = new ClassReader(classData); - reader.accept(visitor, 0); - classData.close(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - public static void acceptVisitor(String className, ClassVisitor visitor) { - acceptVisitor(getClassData(className), visitor); - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ReturnCondition.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ReturnCondition.java deleted file mode 100644 index 2f333d38b..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ReturnCondition.java +++ /dev/null @@ -1,43 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -/** - * В зависимости от этого значения после вызова хук-метода может быть вызван return. - */ - -public enum ReturnCondition { - - /** - * return не вызывается никогда. - */ - NEVER(false), - - /** - * return вызывается всегда. - */ - ALWAYS(false), - - /** - * return вызывается, если хук-метод вернул true. - * Нельзя применить, если хук-метод не возвращает тип boolean. - */ - ON_TRUE(true), - - /** - * return вызывается, если хук-метод вернул null. - * Нельзя применить, если хук-метод возвращает void или примитив. - */ - ON_NULL(true), - - /** - * return вызывается, если хук-метод вернул не null. - * Нельзя применить, если хук-метод возвращает void или примитив. - */ - ON_NOT_NULL(true); - - public final boolean requiresCondition; - - ReturnCondition(boolean requiresCondition) { - this.requiresCondition = requiresCondition; - } - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ReturnValue.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ReturnValue.java deleted file mode 100644 index 3ad322281..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/ReturnValue.java +++ /dev/null @@ -1,40 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - - -/** - * В зависимости от этого значения определяется, что вернёт целевой метод - * при выходе return после вызова хук-метода. - */ -public enum ReturnValue { - - /** - * Возвращается void. - * Используется тогда и только тогда, когда целевой метод возвращает void. - */ - VOID, - - /** - * Возвращается заранее установленное примитичное значение. - * Можно использовать только когда целевой метод возвращает примитив. - */ - PRIMITIVE_CONSTANT, - - /** - * Возвращается null. - * Можно использовать только когда целевой метод возвращает объект. - */ - NULL, - - /** - * Возвращается тот примитив или объект, который вернул хук-метод. - * Можно использовать во всех случаях, кроме того, когда целевой метод возвращает void. - */ - HOOK_RETURN_VALUE, - - /** - * Вызывает другой метод в том же классе и с теми же параметрами, что и хук-метод, но с другим названием. - * Возвращает примитив или объект, который вернул вызванный метод. - */ - ANOTHER_METHOD_RETURN_VALUE - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/SafeClassWriter.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/SafeClassWriter.java deleted file mode 100644 index 034185028..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/SafeClassWriter.java +++ /dev/null @@ -1,37 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.ClassWriter; - -import java.util.ArrayList; - -/** - * ClassWriter с другой реализацией метода getCommonSuperClass: при его использовании не происходит загрузки классов. - * Однако, сама по себе загрузка классов редко является проблемой, потому что инициализация класса (вызов статических - * блоков) происходит не при загрузке класса. Проблемы появляются, когда хуки вставляются в зависимые друг от друга - * классы, тогда стандартная реализация отваливается с ClassCircularityError. - */ -public class SafeClassWriter extends ClassWriter { - - private final ClassMetadataReader classMetadataReader; - - public SafeClassWriter(ClassMetadataReader classMetadataReader, int flags) { - super(flags); - this.classMetadataReader = classMetadataReader; - } - - @Override - protected String getCommonSuperClass(String type1, String type2) { - ArrayList superClasses1 = classMetadataReader.getSuperClasses(type1); - ArrayList superClasses2 = classMetadataReader.getSuperClasses(type2); - int size = Math.min(superClasses1.size(), superClasses2.size()); - int i; - for (i = 0; i < size && superClasses1.get(i).equals(superClasses2.get(i)); i++); - if (i == 0) { - return "java/lang/Object"; - } else { - return superClasses1.get(i-1); - } - } - - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/TypeHelper.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/TypeHelper.java deleted file mode 100644 index ea14466de..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/TypeHelper.java +++ /dev/null @@ -1,90 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -import java.util.HashMap; -import java.util.Map; - -/** - * Класс, позволяющий создавать типы из разных входных данных. - * Эти типы нужны для того, чтобы задавать параметры и возвращаемые значения методов. - */ -public class TypeHelper { - - private static final Map primitiveTypes = new HashMap(9); - - static { - primitiveTypes.put("void", Type.VOID_TYPE); - primitiveTypes.put("boolean", Type.BOOLEAN_TYPE); - primitiveTypes.put("byte", Type.BYTE_TYPE); - primitiveTypes.put("short", Type.SHORT_TYPE); - primitiveTypes.put("char", Type.CHAR_TYPE); - primitiveTypes.put("int", Type.INT_TYPE); - primitiveTypes.put("float", Type.FLOAT_TYPE); - primitiveTypes.put("long", Type.LONG_TYPE); - primitiveTypes.put("double", Type.DOUBLE_TYPE); - } - - /** - * Создает тип по названию класса или примитива. - * Пример использования: getType("net.minecraft.world.World") - вернёт тип для World - * - * @param className необфусцированное название класса - * @return соответствующий тип - */ - public static Type getType(String className) { - return getArrayType(className, 0); - } - - /** - * Создает тип для одномерного массива указанного класса или примитиа. - * Пример использования: getArrayType("net.minecraft.world.World") - вернёт тип для World[] - * - * @param className необфусцированное название класса - * @return соответствующий классу тип одномерного массива - */ - public static Type getArrayType(String className) { - return getArrayType(className, 1); - } - - /** - * Создает тип для n-мерного массива указанного класса или примитива. - * Пример использования: getArrayType("net.minecraft.world.World", 2) - вернёт тип для World[][] - * - * @param className название класса - * @return соответствующий классу тип n-мерного массива - */ - public static Type getArrayType(String className, int arrayDimensions) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < arrayDimensions; i++) { - sb.append("["); - } - Type primitive = primitiveTypes.get(className); - if (primitive == null) { - sb.append("L"); - sb.append(className.replace(".", "/")); - sb.append(";"); - } else { - sb.append(primitive.getDescriptor()); - } - return Type.getType(sb.toString()); - } - - static Object getStackMapFrameEntry(Type type) { - if (type == Type.BOOLEAN_TYPE || type == Type.BYTE_TYPE || type == Type.SHORT_TYPE || - type == Type.CHAR_TYPE || type == Type.INT_TYPE) { - return Opcodes.INTEGER; - } - if (type == Type.FLOAT_TYPE) { - return Opcodes.FLOAT; - } - if (type == Type.DOUBLE_TYPE) { - return Opcodes.DOUBLE; - } - if (type == Type.LONG_TYPE) { - return Opcodes.LONG; - } - return type.getInternalName(); - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/VariableIdHelper.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/VariableIdHelper.java deleted file mode 100644 index 1197a9cf2..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/asm/VariableIdHelper.java +++ /dev/null @@ -1,58 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm; - -import org.objectweb.asm.*; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class VariableIdHelper { - - private static ClassMetadataReader classMetadataReader = new ClassMetadataReader(); - - public static List listLocalVariables(byte[] classData, final String methodName, Type... argTypes) { - final List localVariables = new ArrayList(); - String methodDesc = Type.getMethodDescriptor(Type.VOID_TYPE, argTypes); - final String methodDescWithoutReturnType = methodDesc.substring(0, methodDesc.length() - 1); - - ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) { - - @Override - public MethodVisitor visitMethod(final int acc, String name, String desc, - String signature, String[] exceptions) { - if (methodName.equals(name) && desc.startsWith(methodDescWithoutReturnType)) { - return new MethodVisitor(Opcodes.ASM5) { - @Override - public void visitLocalVariable(String name, String desc, - String signature, Label start, Label end, int index) { - String typeName = Type.getType(desc).getClassName(); - int fixedIndex = index + ((acc & Opcodes.ACC_STATIC) != 0 ? 1 : 0); - localVariables.add(fixedIndex + ": " + typeName + " " + name); - } - }; - } - return null; - } - }; - - classMetadataReader.acceptVisitor(classData, cv); - return localVariables; - } - - public static List listLocalVariables(String className, final String methodName, Type... argTypes) throws IOException { - return listLocalVariables(classMetadataReader.getClassData(className), methodName, argTypes); - } - - public static void printLocalVariables(byte[] classData, String methodName, Type... argTypes) { - List locals = listLocalVariables(classData, methodName, argTypes); - for (String str : locals) { - System.out.println(str); - } - } - - public static void printLocalVariables(String className, String methodName, Type... argTypes) throws IOException { - printLocalVariables(classMetadataReader.getClassData(className), methodName, argTypes); - } - - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/disk/DiskHookLib.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/disk/DiskHookLib.java deleted file mode 100644 index e0ce4b62b..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/disk/DiskHookLib.java +++ /dev/null @@ -1,53 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.disk; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.HookClassTransformer; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class DiskHookLib { - - public static void main(String[] args) throws IOException { - new DiskHookLib().process(); - } - - File untransformedDir = new File("untransformed"); - File transformedDir = new File("transformed"); - File hooksDir = new File("hooks"); - - void process() throws IOException { - HookClassTransformer transformer = new HookClassTransformer(); - for (File file : getFiles(".class", hooksDir)) { - transformer.registerHookContainer(FileUtils.readFileToByteArray(file)); - // теперь file надо скопировать в transformedDir, сохранив путь - } - for (File file : getFiles(".class", untransformedDir)) { - byte[] bytes = IOUtils.toByteArray(new FileInputStream(file)); - String className = ""; //нужно из пути получить название класса через точки вроде ru.lol.DatClass - byte[] newBytes = transformer.transform(className, bytes); - // надо закинуть файл, состоящий из newBytes в transformedDir, сохранив путь - } - } - - private static List getFiles(String postfix, File dir) throws IOException { - ArrayList files = new ArrayList(); - File[] filesArray = dir.listFiles(); - if (filesArray != null) { - for (File file : dir.listFiles()) { - if (file.isDirectory()) { - files.addAll(getFiles(postfix, file)); - } else if (file.getName().toLowerCase().endsWith(postfix)) { - files.add(file); - } - } - } - return files; - } - - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/helper/DictionaryGenerator.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/helper/DictionaryGenerator.java deleted file mode 100644 index 7affb4ef8..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/helper/DictionaryGenerator.java +++ /dev/null @@ -1,43 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.helper; - -import org.apache.commons.io.FileUtils; - -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Генерирует из mcp-шного methods.csv словарь с названиями методов для хуклибы. - * Файл methods.csv лежит в mcp/conf/ - * - * Настоятельно рекомендую сгенерировать methods.bin самостоятельно для своей версии mcp, иначе могут быть - * внезапные ошибки уровня "can not find target method of hook". - */ -public class DictionaryGenerator { - - public static void main(String[] args) throws Exception { - List lines = FileUtils.readLines(new File("C:\\Users\\Quarter\\Documents\\AdvancedRocketry\\build\\extractMappings\\methods.csv")); - lines.remove(0); - HashMap map = new HashMap(); - for (String str : lines) { - String[] splitted = str.split(","); - int first = splitted[0].indexOf('_'); - int second = splitted[0].indexOf('_', first+1); - int id = Integer.valueOf(splitted[0].substring(first+1, second)); - map.put(id, splitted[1]); - } - - DataOutputStream out = new DataOutputStream(new FileOutputStream("C:\\Users\\Quarter\\Documents\\methods.bin")); - out.writeInt(map.size()); - - for (Map.Entry entry : map.entrySet()) { - out.writeInt(entry.getKey()); - out.writeUTF(entry.getValue()); - } - - out.close(); - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/DeobfuscationMetadataReader.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/DeobfuscationMetadataReader.java deleted file mode 100644 index d7a97c1c9..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/DeobfuscationMetadataReader.java +++ /dev/null @@ -1,91 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.minecraft; - -import net.minecraft.launchwrapper.Launch; -import net.minecraft.launchwrapper.LaunchClassLoader; -import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.ClassMetadataReader; - -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Еще больше костылей вдобавок к ClassMetadataReader для работы с майновской обфускацией. - */ -public class DeobfuscationMetadataReader extends ClassMetadataReader { - - private static Method runTransformers; - - static { - try { - runTransformers = LaunchClassLoader.class.getDeclaredMethod("runTransformers", - String.class, String.class, byte[].class); - runTransformers.setAccessible(true); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public byte[] getClassData(String className) throws IOException { - byte[] bytes = super.getClassData(unmap(className.replace('.', '/'))); - return deobfuscateClass(className, bytes); - } - - @Override - protected boolean checkSameMethod(String sourceName, String sourceDesc, String targetName, String targetDesc) { - return checkSameMethod(sourceName, targetName) && sourceDesc.equals(targetDesc); - } - - // Фордж и прочее могут своими патчами добавлять методы, которые нужно уметь оверрайдить хуками. - // Для этого приходится применять трансформеры во время поиска супер-методов - // этот метод должен вызываться только во время загрузки сабклассов проверяемого класса, - // так что все должно быть норм - @Override - protected MethodReference getMethodReferenceASM(String type, String methodName, String desc) throws IOException { - FindMethodClassVisitor cv = new FindMethodClassVisitor(methodName, desc); - byte[] bytes = getTransformedBytes(type); - acceptVisitor(bytes, cv); - return cv.found ? new MethodReference(type, cv.targetName, cv.targetDesc) : null; - } - - static byte[] deobfuscateClass(String className, byte[] bytes) { - if (HookLoader.getDeobfuscationTransformer() != null) { - bytes = HookLoader.getDeobfuscationTransformer().transform(className, className, bytes); - } - return bytes; - } - - private static byte[] getTransformedBytes(String type) throws IOException { - String obfName = unmap(type); - byte[] bytes = Launch.classLoader.getClassBytes(obfName); - if (bytes == null) { - throw new RuntimeException("Bytes for " + obfName + " not found"); - } - try { - bytes = (byte[]) runTransformers.invoke(Launch.classLoader, obfName, type, bytes); - } catch (Exception e) { - e.printStackTrace(); - } - return bytes; - } - - // возвращает из необфусцированного названия типа обфусцированное - private static String unmap(String type) { - if (HookLibPlugin.getObfuscated()) { - return FMLDeobfuscatingRemapper.INSTANCE.unmap(type); - } - return type; - } - - private static boolean checkSameMethod(String srgName, String mcpName) { - if (HookLibPlugin.getObfuscated() && MinecraftClassTransformer.instance != null) { - int methodId = MinecraftClassTransformer.getMethodId(srgName); - String remappedName = MinecraftClassTransformer.instance.getMethodNames().get(methodId); - if (remappedName != null && remappedName.equals(mcpName)) { - return true; - } - } - return srgName.equals(mcpName); - } - -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/HookLibPlugin.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/HookLibPlugin.java deleted file mode 100644 index aba41bc64..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/HookLibPlugin.java +++ /dev/null @@ -1,57 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.minecraft; - -import net.minecraftforge.fml.relauncher.CoreModManager; -import net.minecraftforge.fml.relauncher.FMLRelaunchLog; -import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; - -import java.lang.reflect.Field; -import java.util.Map; - -public class HookLibPlugin implements IFMLLoadingPlugin { - - private static boolean obf; - private static boolean checked; - - // 1.6.x only - public String[] getLibraryRequestClass() { - return null; - } - - // 1.7.x only - public String getAccessTransformerClass() { - return null; - } - - @Override - public String[] getASMTransformerClass() { - return new String[]{PrimaryClassTransformer.class.getName()}; - } - - @Override - public String getModContainerClass() { - return null; - } - - @Override - public String getSetupClass() { - return null; - } - - @Override - public void injectData(Map data) {} - - public static boolean getObfuscated() { - if (!checked) { - try { - Field deobfField = CoreModManager.class.getDeclaredField("deobfuscatedEnvironment"); - deobfField.setAccessible(true); - obf = !deobfField.getBoolean(null); - FMLRelaunchLog.info("[HOOKLIB] " + " Obfuscated: " + obf); - } catch (Exception e) { - e.printStackTrace(); - } - checked = true; - } - return obf; - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/HookLoader.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/HookLoader.java deleted file mode 100644 index c90447da2..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/HookLoader.java +++ /dev/null @@ -1,86 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.minecraft; - -import net.minecraftforge.fml.common.asm.transformers.DeobfuscationTransformer; -import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.AsmHook; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.ClassMetadataReader; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.HookClassTransformer; - -import java.util.Map; - -/** - * Удобная базовая реализация IFMLLoadingPlugin для использования HookLib. - * Регистрировать хуки и контейнеры нужно в registerHooks(). - */ -public abstract class HookLoader implements IFMLLoadingPlugin { - - private static DeobfuscationTransformer deobfuscationTransformer; - - private static ClassMetadataReader deobfuscationMetadataReader; - - static { - deobfuscationMetadataReader = new DeobfuscationMetadataReader(); - } - - public static HookClassTransformer getTransformer() { - return PrimaryClassTransformer.instance.registeredSecondTransformer ? - MinecraftClassTransformer.instance : PrimaryClassTransformer.instance; - } - - /** - * Регистрирует вручную созданный хук - */ - public static void registerHook(AsmHook hook) { - getTransformer().registerHook(hook); - } - - /** - * Деобфусцирует класс с хуками и регистрирует хуки из него - */ - public static void registerHookContainer(String className) { - getTransformer().registerHookContainer(className); - } - - public static ClassMetadataReader getDeobfuscationMetadataReader() { - return deobfuscationMetadataReader; - } - - static DeobfuscationTransformer getDeobfuscationTransformer() { - if (HookLibPlugin.getObfuscated() && deobfuscationTransformer == null) { - deobfuscationTransformer = new DeobfuscationTransformer(); - } - return deobfuscationTransformer; - } - - // 1.6.x only - public String[] getLibraryRequestClass() { - return null; - } - - // 1.7.x only - public String getAccessTransformerClass() { - return null; - } - - @Override - public String[] getASMTransformerClass() { - return null; - } - - @Override - public String getModContainerClass() { - return null; - } - - @Override - public String getSetupClass() { - return null; - } - - @Override - public void injectData(Map data) { - registerHooks(); - } - - protected abstract void registerHooks(); -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/MinecraftClassTransformer.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/MinecraftClassTransformer.java deleted file mode 100644 index aa918b8e4..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/MinecraftClassTransformer.java +++ /dev/null @@ -1,108 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.minecraft; - -import net.minecraft.launchwrapper.IClassTransformer; -import org.objectweb.asm.ClassWriter; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.AsmHook; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.HookClassTransformer; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.HookInjectorClassVisitor; - -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Этот трансформер занимается вставкой хуков с момента запуска майнкрафта. Здесь сосредоточены все костыли, - * которые необходимы для правильной работы с обфусцированными названиями методов. - */ -public class MinecraftClassTransformer extends HookClassTransformer implements IClassTransformer { - - static MinecraftClassTransformer instance; - private Map methodNames; - - private static List postTransformers = new ArrayList(); - - public MinecraftClassTransformer() { - instance = this; - - if (HookLibPlugin.getObfuscated()) { - try { - long timeStart = System.currentTimeMillis(); - methodNames = loadMethodNames(); - long time = System.currentTimeMillis() - timeStart; - logger.debug("Methods dictionary loaded in " + time + " ms"); - } catch (IOException e) { - logger.severe("Can not load obfuscated method names", e); - } - } - - this.classMetadataReader = HookLoader.getDeobfuscationMetadataReader(); - - this.hooksMap.putAll(PrimaryClassTransformer.instance.getHooksMap()); - PrimaryClassTransformer.instance.getHooksMap().clear(); - PrimaryClassTransformer.instance.registeredSecondTransformer = true; - } - - private HashMap loadMethodNames() throws IOException { - InputStream resourceStream = getClass().getResourceAsStream("/methods.bin"); - if (resourceStream == null) throw new IOException("Methods dictionary not found"); - DataInputStream input = new DataInputStream(new BufferedInputStream(resourceStream)); - int numMethods = input.readInt(); - HashMap map = new HashMap(numMethods); - for (int i = 0; i < numMethods; i++) { - map.put(input.readInt(), input.readUTF()); - } - input.close(); - return map; - } - - @Override - public byte[] transform(String oldName, String newName, byte[] bytecode) { - bytecode = transform(newName, bytecode); - for (int i = 0; i < postTransformers.size(); i++) { - bytecode = postTransformers.get(i).transform(oldName, newName, bytecode); - } - return bytecode; - } - - @Override - protected HookInjectorClassVisitor createInjectorClassVisitor(ClassWriter cw, List hooks) { - return new HookInjectorClassVisitor(this, cw, hooks) { - @Override - protected boolean isTargetMethod(AsmHook hook, String name, String desc) { - if (HookLibPlugin.getObfuscated()) { - String mcpName = methodNames.get(getMethodId(name)); - if (mcpName != null && super.isTargetMethod(hook, mcpName, desc)) { - return true; - } - } - return super.isTargetMethod(hook, name, desc); - } - }; - } - - public Map getMethodNames() { - return methodNames; - } - - public static int getMethodId(String srgName) { - if (srgName.startsWith("func_")) { - int first = srgName.indexOf('_'); - int second = srgName.indexOf('_', first + 1); - return Integer.valueOf(srgName.substring(first + 1, second)); - } else { - return -1; - } - } - - /** - * Регистрирует трансформер, который будет запущен после обычных, и в том числе после деобфусцирующего трансформера. - */ - public static void registerPostTransformer(IClassTransformer transformer) { - postTransformers.add(transformer); - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/PrimaryClassTransformer.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/PrimaryClassTransformer.java deleted file mode 100644 index 6c7455b28..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/PrimaryClassTransformer.java +++ /dev/null @@ -1,96 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.minecraft; - -import net.minecraft.launchwrapper.IClassTransformer; -import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Type; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.AsmHook; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.HookClassTransformer; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.HookInjectorClassVisitor; - -import java.util.HashMap; -import java.util.List; - -/** Этим трансформером трансформятся все классы, которые грузятся раньше майновских. - * В момент начала загрузки майна (точнее, чуть раньше - в Loader.injectData) все хуки отсюда переносятся в - * MinecraftClassTransformer. Такой перенос нужен, чтобы трансформеры хуклибы применялись последними - в частности, - * после деобфускации, которую делает фордж. - */ -public class PrimaryClassTransformer extends HookClassTransformer implements IClassTransformer { - - // костыль для случая, когда другой мод дергает хуклиб раньше, чем она запустилась - static PrimaryClassTransformer instance = new PrimaryClassTransformer(); - boolean registeredSecondTransformer; - - public PrimaryClassTransformer() { - this.classMetadataReader = HookLoader.getDeobfuscationMetadataReader(); - - if (instance != null) { - // переносим хуки, которые уже успели нарегистрировать - this.hooksMap.putAll(PrimaryClassTransformer.instance.getHooksMap()); - PrimaryClassTransformer.instance.getHooksMap().clear(); - } else { - registerHookContainer(SecondaryTransformerHook.class.getName()); - } - instance = this; - } - - @Override - public byte[] transform(String oldName, String newName, byte[] bytecode) { - return transform(newName, bytecode); - } - - @Override - protected HookInjectorClassVisitor createInjectorClassVisitor(ClassWriter cw, List hooks) { - // Если ничего не сломается, то никакие майновские классы не должны грузиться этим трансформером - - // соответственно, и костыли для деобфускации названий методов тут не нужны. - return new HookInjectorClassVisitor(this, cw, hooks) { - @Override - protected boolean isTargetMethod(AsmHook hook, String name, String desc) { - return super.isTargetMethod(hook, name, mapDesc(desc)); - } - }; - } - - HashMap> getHooksMap() { - return hooksMap; - } - - static String mapDesc(String desc) { - if (!HookLibPlugin.getObfuscated()) return desc; - - Type methodType = Type.getMethodType(desc); - Type mappedReturnType = map(methodType.getReturnType()); - Type[] argTypes = methodType.getArgumentTypes(); - Type[] mappedArgTypes = new Type[argTypes.length]; - for (int i = 0; i < mappedArgTypes.length; i++) { - mappedArgTypes[i] = map(argTypes[i]); - } - return Type.getMethodDescriptor(mappedReturnType, mappedArgTypes); - } - - static Type map(Type type) { - if (!HookLibPlugin.getObfuscated()) return type; - - // void or primitive - if (type.getSort() < 9) return type; - - //array - if (type.getSort() == 9) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < type.getDimensions(); i++) { - sb.append("["); - } - boolean isPrimitiveArray = type.getSort() < 9; - if (!isPrimitiveArray) sb.append("L"); - sb.append(map(type.getElementType()).getInternalName()); - if (!isPrimitiveArray) sb.append(";"); - return Type.getType(sb.toString()); - } else if (type.getSort() == 10) { - String unmappedName = FMLDeobfuscatingRemapper.INSTANCE.map(type.getInternalName()); - return Type.getType("L" + unmappedName + ";"); - } else { - throw new IllegalArgumentException("Can not map method type!"); - } - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/SecondaryTransformerHook.java b/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/SecondaryTransformerHook.java deleted file mode 100644 index 7fab719bc..000000000 --- a/src/main/java/zmaster587/advancedRocketry/repack/gloomyfolken/hooklib/minecraft/SecondaryTransformerHook.java +++ /dev/null @@ -1,22 +0,0 @@ -package zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.minecraft; - -import net.minecraft.launchwrapper.LaunchClassLoader; -import net.minecraftforge.fml.common.Loader; -import zmaster587.advancedRocketry.repack.gloomyfolken.hooklib.asm.Hook; - -public class SecondaryTransformerHook { - - /** - * Регистрирует хук-трансформер последним. - */ - @Hook - public static void injectData(Loader loader, Object... data) { - ClassLoader classLoader = SecondaryTransformerHook.class.getClassLoader(); - if (classLoader instanceof LaunchClassLoader) { - ((LaunchClassLoader)classLoader).registerTransformer(MinecraftClassTransformer.class.getName()); - } else { - System.out.println("HookLib was not loaded by LaunchClassLoader. Hooks will not be injected."); - } - } - -} diff --git a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteBiomeChanger.java b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteBiomeChanger.java index 67639e104..c87a1c586 100644 --- a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteBiomeChanger.java +++ b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteBiomeChanger.java @@ -13,6 +13,7 @@ import zmaster587.advancedRocketry.api.satellite.SatelliteProperties; import zmaster587.advancedRocketry.item.ItemBiomeChanger; import zmaster587.advancedRocketry.util.BiomeHandler; +import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.api.IUniversalEnergy; import zmaster587.libVulpes.util.HashedBlockPosition; @@ -64,7 +65,7 @@ public String getInfo(World world) { @Override public String getName() { - return "Biome Changer"; + return LibVulpes.proxy.getLocalizedString("item.satellite.biomechanger"); } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteComposition.java b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteComposition.java index d5d844b50..df59cc861 100644 --- a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteComposition.java +++ b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteComposition.java @@ -1,6 +1,7 @@ package zmaster587.advancedRocketry.satellite; import zmaster587.advancedRocketry.api.DataStorage; +import zmaster587.libVulpes.LibVulpes; public class SatelliteComposition extends SatelliteData { @@ -12,7 +13,7 @@ public SatelliteComposition() { @Override public String getName() { - return "Composition Scanner"; + return LibVulpes.proxy.getLocalizedString("item.satellite.composition"); } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteMassScanner.java b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteMassScanner.java index e5b5f8b05..17e2d0edd 100644 --- a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteMassScanner.java +++ b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteMassScanner.java @@ -1,6 +1,7 @@ package zmaster587.advancedRocketry.satellite; import zmaster587.advancedRocketry.api.DataStorage; +import zmaster587.libVulpes.LibVulpes; public class SatelliteMassScanner extends SatelliteData { @@ -12,7 +13,7 @@ public SatelliteMassScanner() { @Override public String getName() { - return "Mass Scanner"; + return LibVulpes.proxy.getLocalizedString("item.satellite.massscanner"); } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteMicrowaveEnergy.java b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteMicrowaveEnergy.java index ecea02320..d69999837 100644 --- a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteMicrowaveEnergy.java +++ b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteMicrowaveEnergy.java @@ -34,7 +34,7 @@ public String getInfo(World world) { @Override public String getName() { - return "Microwave Energy Satellite"; + return LibVulpes.proxy.getLocalizedString("item.satellite.solar"); } @Override @@ -48,20 +48,30 @@ public double failureChance() { } @Override - public int getEnergyMTU(EnumFacing side) { - return (int) (ARConfiguration.getCurrentConfig().microwaveRecieverMulitplier * battery.extractEnergy(battery.getMaxEnergyStored(), false)); + public void setDimensionId(World world) { + super.setDimensionId(world); } @Override - public void setDimensionId(World world) { - super.setDimensionId(world); + public int getEnergyMTU(EnumFacing side) { + return transmitEnergy(side, true); } @Override public int transmitEnergy(EnumFacing dir, boolean simulate) { - return getEnergyMTU(EnumFacing.DOWN); + + // cap by generation per tick (after upkeep) + int genPerTick = Math.max(0, getPowerPerTick() - 1); + + int maxSend = (int)Math.round( + ARConfiguration.getCurrentConfig().microwaveRecieverMulitplier * genPerTick + ); + + return battery.extractEnergy(maxSend, simulate); } + + @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); diff --git a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteOptical.java b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteOptical.java index 1142b2ce3..256599630 100644 --- a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteOptical.java +++ b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteOptical.java @@ -2,6 +2,7 @@ import zmaster587.advancedRocketry.api.DataStorage; import zmaster587.advancedRocketry.api.DataStorage.DataType; +import zmaster587.libVulpes.LibVulpes; public class SatelliteOptical extends SatelliteData { @@ -13,7 +14,7 @@ public SatelliteOptical() { @Override public String getName() { - return "Optical Telescope"; + return LibVulpes.proxy.getLocalizedString("item.satellite.opticaltelescope"); } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteOreMapping.java b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteOreMapping.java index f536587b9..a0e5c3347 100644 --- a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteOreMapping.java +++ b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteOreMapping.java @@ -13,6 +13,7 @@ import zmaster587.advancedRocketry.api.satellite.SatelliteBase; import zmaster587.advancedRocketry.api.satellite.SatelliteProperties; import zmaster587.advancedRocketry.item.ItemOreScanner; +import zmaster587.libVulpes.LibVulpes; import javax.annotation.Nonnull; import java.util.ArrayList; @@ -209,7 +210,7 @@ public double failureChance() { @Override public String getName() { - return "Ore Mapper"; + return LibVulpes.proxy.getLocalizedString("item.satellite.oremapper"); } diff --git a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteWeatherController.java b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteWeatherController.java index d2d3676cf..b9b8b8844 100644 --- a/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteWeatherController.java +++ b/src/main/java/zmaster587/advancedRocketry/satellite/SatelliteWeatherController.java @@ -27,6 +27,7 @@ import zmaster587.advancedRocketry.network.PacketAirParticle; import zmaster587.advancedRocketry.network.PacketFluidParticle; import zmaster587.advancedRocketry.util.BiomeHandler; +import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.api.IUniversalEnergy; import zmaster587.libVulpes.network.PacketHandler; import zmaster587.libVulpes.util.HashedBlockPosition; @@ -72,7 +73,7 @@ public String getInfo(World world) { @Override public String getName() { - return "Weather Satellite"; + return LibVulpes.proxy.getLocalizedString("item.satellite.weather"); } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/stations/SpaceObjectBase.java b/src/main/java/zmaster587/advancedRocketry/stations/SpaceObjectBase.java index b20962060..b74086e33 100644 --- a/src/main/java/zmaster587/advancedRocketry/stations/SpaceObjectBase.java +++ b/src/main/java/zmaster587/advancedRocketry/stations/SpaceObjectBase.java @@ -254,7 +254,7 @@ public void writeToNbt(NBTTagCompound nbt) { nbt.setInteger("id", getId()); nbt.setInteger("posX", posX); nbt.setInteger("posY", posY); - nbt.setInteger("alitude", altitude); + nbt.setInteger("altitude", altitude); nbt.setInteger("spawnX", spawnLocation.x); nbt.setInteger("spawnY", spawnLocation.y); nbt.setInteger("spawnZ", spawnLocation.z); diff --git a/src/main/java/zmaster587/advancedRocketry/stations/SpaceStationObject.java b/src/main/java/zmaster587/advancedRocketry/stations/SpaceStationObject.java index 10340153f..0be61dc0c 100644 --- a/src/main/java/zmaster587/advancedRocketry/stations/SpaceStationObject.java +++ b/src/main/java/zmaster587/advancedRocketry/stations/SpaceStationObject.java @@ -57,9 +57,10 @@ public class SpaceStationObject implements ISpaceObject, IPlanetDefiner { private boolean isAnchored = false; private double[] rotation; private double[] angularVelocity; - private long lastTimeModification = 0; + private final long[] lastTimeModification = new long[3]; // one per axis private DimensionProperties properties; + public SpaceStationObject() { properties = (DimensionProperties) zmaster587.advancedRocketry.dimension.DimensionManager.defaultSpaceDimensionProperties.clone(); orbitalDistance = 50.0f; @@ -75,8 +76,12 @@ public SpaceStationObject() { knownPlanetList = new HashSet<>(); angularVelocity = new double[3]; rotation = new double[3]; - } - + long now = getWorldTime(); + lastTimeModification[0] = now; + lastTimeModification[1] = now; + lastTimeModification[2] = now; + } + public Set getKnownPlanetList() { return knownPlanetList; } @@ -110,6 +115,17 @@ public void discoverPlanet(int pid) { PacketHandler.sendToAll(new PacketSpaceStationInfo(getId(), this)); } + public void applyRemoteRotationState(double rx, double ry, double rz, + double drx, double dry, double drz) { + rotation[0] = rx; rotation[1] = ry; rotation[2] = rz; + angularVelocity[0] = drx; angularVelocity[1] = dry; angularVelocity[2] = drz; + long now = getWorldTime(); + lastTimeModification[0] = now; + lastTimeModification[1] = now; + lastTimeModification[2] = now; + } + + /** * @return id of the space object (NOT the DIMID) */ @@ -209,8 +225,12 @@ public int getAltitude() { * @return rotation of the station in degrees */ public double getRotation(EnumFacing dir) { - - return (rotation[getIDFromDir(dir)] + getDeltaRotation(dir) * (getWorldTime() - lastTimeModification)) % (360D); + int idx = getIDFromDir(dir); + long dt = getWorldTime() - lastTimeModification[idx]; + double a = rotation[idx] + angularVelocity[idx] * dt; + // keep modulo stable + a = ((a % 360D) + 360D) % 360D; + return a; } /** @@ -241,8 +261,10 @@ else if (facing == EnumFacing.UP) /** * @param rotation rotation of the station in degrees */ - public void setRotation(double rotation, EnumFacing facing) { - this.rotation[getIDFromDir(facing)] = rotation; + public void setRotation(double rotDeg, EnumFacing facing) { + int idx = getIDFromDir(facing); + rotation[idx] = rotDeg; + lastTimeModification[idx] = getWorldTime(); } /** @@ -255,15 +277,17 @@ public double getDeltaRotation(EnumFacing facing) { /** * @param rotation anglarVelocity of the station in degrees per tick */ - public void setDeltaRotation(double rotation, EnumFacing facing) { + public void setDeltaRotation(double newVel, EnumFacing facing) { if (!isAnchored()) { - this.rotation[getIDFromDir(facing)] = getRotation(facing); - this.lastTimeModification = getWorldTime(); - - this.angularVelocity[getIDFromDir(facing)] = rotation; + int idx = getIDFromDir(facing); + // capture current integrated angle as the new snapshot + rotation[idx] = getRotation(facing); + lastTimeModification[idx] = getWorldTime(); + angularVelocity[idx] = newVel; } } + public double getMaxRotationalAcceleration() { return 0.02D; } @@ -683,7 +707,7 @@ public void writeToNbt(NBTTagCompound nbt) { nbt.setInteger("posX", posX); nbt.setInteger("posY", posZ); nbt.setBoolean("created", created); - nbt.setInteger("alitude", altitude); + nbt.setInteger("altitude", altitude); nbt.setInteger("spawnX", spawnLocation.x); nbt.setInteger("spawnY", spawnLocation.y); nbt.setInteger("spawnZ", spawnLocation.z); diff --git a/src/main/java/zmaster587/advancedRocketry/tile/TileFluidTank.java b/src/main/java/zmaster587/advancedRocketry/tile/TileFluidTank.java index faddcb90f..c0843e49f 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/TileFluidTank.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/TileFluidTank.java @@ -8,11 +8,13 @@ import net.minecraft.util.EnumFacing; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidTankProperties; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; import zmaster587.advancedRocketry.world.util.WorldDummy; import zmaster587.libVulpes.tile.multiblock.hatch.TileFluidHatch; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class TileFluidTank extends TileFluidHatch { @@ -20,6 +22,11 @@ public class TileFluidTank extends TileFluidHatch { private long lastUpdateTime; private boolean fluidChanged; + private boolean removing = false; + private boolean inColumnOp = false; + + public void setRemoving(boolean removing) { this.removing = removing; } + public TileFluidTank() { super(); fluidChanged = false; @@ -30,15 +37,56 @@ public TileFluidTank(int i) { fluidChanged = false; } + // Single, reusable delegating handler (column-aware) + private final net.minecraftforge.fluids.capability.IFluidHandler selfHandler = + new net.minecraftforge.fluids.capability.IFluidHandler() { + @Override public int fill(FluidStack r, boolean doFill) { return TileFluidTank.this.fill(r, doFill); } + @Override public FluidStack drain(FluidStack r, boolean doDrain) { return TileFluidTank.this.drain(r, doDrain); } + @Override public FluidStack drain(int max, boolean doDrain) { return TileFluidTank.this.drain(max, doDrain); } + @Override public IFluidTankProperties[] getTankProperties() { return TileFluidTank.this.getTankProperties(); } + }; + + @Override + public boolean hasCapability(net.minecraftforge.common.capabilities.Capability cap, + @Nullable EnumFacing side) { + if (cap == net.minecraftforge.fluids.capability.CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) { + return true; // expose our own handler for fluids, all sides + } + return super.hasCapability(cap, side); // items and anything else come from the parent + } + + + @Override + @SuppressWarnings("unchecked") + public T getCapability(net.minecraftforge.common.capabilities.Capability cap, + @Nullable EnumFacing side) { + if (cap == net.minecraftforge.fluids.capability.CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) { + return (T) selfHandler; // never fall back to parent for fluids + } + return super.getCapability(cap, side); // items etc. still via TileFluidHatch (EmbeddedInventory) + } + + + + private void checkForUpdate() { - if (fluidChanged && world instanceof WorldDummy || world.getTotalWorldTime() - lastUpdateTime > MAX_UPDATE) { - this.markDirty(); + if (world == null) return; + if (fluidChanged && (world instanceof WorldDummy || world.getTotalWorldTime() - lastUpdateTime > MAX_UPDATE)) { + markDirty(); world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); lastUpdateTime = world.getTotalWorldTime(); fluidChanged = false; } } + private boolean enterColumnOp() { + if (inColumnOp) return false; + inColumnOp = true; + return true; + } + private void exitColumnOp() { inColumnOp = false; } + + @Override public SPacketUpdateTileEntity getUpdatePacket() { return new SPacketUpdateTileEntity(getPos(), getBlockMetadata(), getUpdateTag()); @@ -49,12 +97,24 @@ public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { readFromNBT(pkt.getNbtCompound()); } + @Override + public NBTTagCompound getUpdateTag() { + return writeToNBT(new NBTTagCompound()); + } + + @Override + public void handleUpdateTag(NBTTagCompound tag) { + readFromNBT(tag); + } + @Override public int fill(FluidStack resource, boolean doFill) { if (resource == null) return 0; + if (world == null || world.isRemote || removing) return 0; + TileFluidTank handler2 = this.getFluidTankInDirection(EnumFacing.UP); //Move up, check if we can fill there, do top down @@ -79,9 +139,10 @@ private int fillInternal2(FluidStack resource, boolean doFill) { if (resource2.amount > 0) amt += super.fill(resource2, doFill); - if (amt > 0 && doFill) + if (amt > 0 && doFill) { fluidChanged = true; - + markDirty(); + } checkForUpdate(); return amt; @@ -92,16 +153,28 @@ public String getModularInventoryName() { return AdvancedRocketryBlocks.blockPressureTank.getLocalizedName(); } + @Nullable + public FluidStack getOwnContentsCopy() { + FluidStack f = fluidTank.getFluid(); + return f == null ? null : f.copy(); + } + + @Override public FluidStack drain(int maxDrain, boolean doDrain) { + if (world == null || world.isRemote || removing) return null; + IFluidHandler handler = this.getFluidTankInDirection(EnumFacing.UP); FluidStack fStack = null; - if (handler != null && handler.getTankProperties()[0].getContents() != null && - fluidTank.getFluid() != null && fluidTank.getFluid().getFluid() == - handler.getTankProperties()[0].getContents().getFluid()) { - - fStack = handler.drain(maxDrain, doDrain); + if (handler != null) { + IFluidTankProperties[] props = handler.getTankProperties(); + FluidStack contents = (props != null && props.length > 0) ? props[0].getContents() : null; + if (contents != null && + fluidTank.getFluid() != null && + fluidTank.getFluid().getFluid() == contents.getFluid()) { + fStack = handler.drain(maxDrain, doDrain); + } } if (fStack != null) return fStack; @@ -110,6 +183,7 @@ public FluidStack drain(int maxDrain, boolean doDrain) { if (fStack2 != null && doDrain) { fluidChanged = true; + markDirty(); } checkForUpdate(); @@ -118,8 +192,8 @@ public FluidStack drain(int maxDrain, boolean doDrain) { } @Override - public FluidStack drain(FluidStack resource, - boolean doDrain) { + public FluidStack drain(FluidStack resource, boolean doDrain) { + if (world == null || world.isRemote || removing || resource == null) return null; if (this.fluidTank.getFluid() == null || resource.getFluid() != this.fluidTank.getFluid().getFluid()) return null; @@ -127,6 +201,7 @@ public FluidStack drain(FluidStack resource, } public TileFluidTank getFluidTankInDirection(EnumFacing direction) { + if (world == null) return null; TileEntity tile = world.getTileEntity(pos.offset(direction)); if (tile instanceof TileFluidTank) { @@ -161,30 +236,134 @@ protected boolean useBucket(int slot, @Nonnull ItemStack stack) { if (bucketUsed) { IFluidHandler handler = getFluidTankInDirection(EnumFacing.DOWN); if (handler != null) { - FluidStack othertank = handler.getTankProperties()[0].getContents(); - if (othertank == null || (othertank.amount < handler.getTankProperties()[0].getCapacity())) - fluidTank.drain(handler.fill(fluidTank.getFluid(), true), true); + IFluidTankProperties[] props = handler.getTankProperties(); + FluidStack contents = (props != null && props.length > 0) ? props[0].getContents() : null; + int capacity = (props != null && props.length > 0) ? props[0].getCapacity() : 0; + + // If the tank below is empty or has room, push fluid down + if (contents == null || (capacity > 0 && contents.amount < capacity)) { + FluidStack ours = fluidTank.getFluid(); + if (ours != null && ours.amount > 0) { + int canMove = handler.fill(new FluidStack(ours.getFluid(), ours.amount), false); + if (canMove > 0) { + FluidStack drained = fluidTank.drain(canMove, true); + int filled = handler.fill(drained, true); + if (filled < drained.amount) { + fluidTank.fill(new FluidStack(drained.getFluid(), drained.amount - filled), true); + } + fluidChanged = true; + markDirty(); + checkForUpdate(); + } + } + } } } return bucketUsed; } - public void onAdjacentBlockUpdated(EnumFacing dir) { - if (dir != EnumFacing.DOWN) - return; + @Override + public void invalidate() { + removing = true; + super.invalidate(); + } - TileFluidTank tank = getFluidTankInDirection(EnumFacing.UP); + @Override + public void onChunkUnload() { + removing = true; + super.onChunkUnload(); + } - if (tank != null && tank.getTankProperties()[0].getContents() != null) { - if (fluidTank.getFluid() == null) { - fluidTank.fill(tank.fluidTank.drain(fluidTank.getCapacity(), true), true); - } else if (tank.getTankProperties()[0].getContents().getFluid() == fluidTank.getFluid().getFluid()) { - fluidTank.fill(tank.drain(fluidTank.getCapacity() - fluidTank.getFluidAmount(), true), true); - tank.fluidTank.drain(fluidTank.getCapacity() - fluidTank.getFluidAmount(), true); + public void onAdjacentBlockUpdated(EnumFacing dir) { + if (world == null || world.isRemote || removing) return; + if (!enterColumnOp()) return; + try { + // If the block BELOW changed, push our fluid down into it and cascade. + if (dir == EnumFacing.DOWN) { + TileFluidTank down = getFluidTankInDirection(EnumFacing.DOWN); + if (down != null) { + FluidStack ours = fluidTank.getFluid(); + if (ours != null && ours.amount > 0) { + IFluidTankProperties[] props = down.getTankProperties(); + FluidStack below = (props != null && props.length > 0) ? props[0].getContents() : null; + boolean compatible = (below == null) || (below.getFluid() == ours.getFluid()); + + if (compatible) { + int room = down.fluidTank.getCapacity() - down.fluidTank.getFluidAmount(); + if (room > 0) { + int toMove = Math.min(room, ours.amount); + + // Use capability: simulate then commit + IFluidHandler downH = down.getCapability( + net.minecraftforge.fluids.capability.CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, + EnumFacing.UP + ); + if (downH != null) { + int canFill = downH.fill(new FluidStack(ours.getFluid(), toMove), false); + if (canFill > 0) { + FluidStack drained = fluidTank.drain(canFill, true); + int filled = downH.fill(drained, true); + if (filled < drained.amount) { + // Put any remainder back to avoid loss + fluidTank.fill(new FluidStack(drained.getFluid(), drained.amount - filled), true); + } + fluidChanged = true; + markDirty(); + checkForUpdate(); + down.markDirty(); + down.checkForUpdate(); + + // Cascade to the next tank down + down.onAdjacentBlockUpdated(EnumFacing.DOWN); + } + } + } + } + } + } + } + // If the block ABOVE changed, pull fluid from the tank above into THIS tank (column-aware). + else if (dir == EnumFacing.UP) { + TileFluidTank up = getFluidTankInDirection(EnumFacing.UP); + if (up != null) { + IFluidTankProperties[] props = up.getTankProperties(); + FluidStack above = (props != null && props.length > 0) ? props[0].getContents() : null; + + if (above != null) { + if (fluidTank.getFluid() == null) { + // We're empty: pull directly from the upper tank's internal store + FluidStack moved = up.drain(fluidTank.getCapacity(), true); + if (moved != null && moved.amount > 0) { + fluidTank.fill(moved, true); + fluidChanged = true; + markDirty(); + checkForUpdate(); + } + } else if (above.getFluid() == fluidTank.getFluid().getFluid()) { + // Same fluid: do a column-aware pull from the upper tank + int room = fluidTank.getCapacity() - fluidTank.getFluidAmount(); + if (room > 0) { + FluidStack moved = up.drain(room, true); + if (moved != null && moved.amount > 0) { + fluidTank.fill(moved, true); + fluidChanged = true; + markDirty(); + checkForUpdate(); + } + } + } + } + } } - this.markDirty(); + // Final safety: ensure any state change is persisted/sent + if (fluidChanged) { + this.markDirty(); + checkForUpdate(); + } + } finally { + exitColumnOp(); } } } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/TileOrbitalRegistry.java b/src/main/java/zmaster587/advancedRocketry/tile/TileOrbitalRegistry.java new file mode 100644 index 000000000..77c1e03f0 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/tile/TileOrbitalRegistry.java @@ -0,0 +1,1554 @@ +package zmaster587.advancedRocketry.tile; + +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fml.relauncher.Side; +import zmaster587.advancedRocketry.AdvancedRocketry; +import zmaster587.advancedRocketry.api.SatelliteRegistry; +import zmaster587.advancedRocketry.api.satellite.SatelliteBase; +import zmaster587.advancedRocketry.api.satellite.SatelliteProperties; +import zmaster587.advancedRocketry.api.stations.ISpaceObject; +import zmaster587.advancedRocketry.dimension.DimensionManager; +import zmaster587.advancedRocketry.dimension.DimensionProperties; +import zmaster587.advancedRocketry.inventory.TextureResources; +import zmaster587.advancedRocketry.item.ItemOreScanner; +import zmaster587.advancedRocketry.item.ItemSatelliteIdentificationChip; +import zmaster587.advancedRocketry.item.ItemStationChip; +import zmaster587.advancedRocketry.satellite.SatelliteData; +import zmaster587.advancedRocketry.stations.SpaceObjectManager; +import zmaster587.advancedRocketry.stations.SpaceStationObject; +import zmaster587.advancedRocketry.util.StationLandingLocation; + +import zmaster587.libVulpes.client.util.ProgressBarImage; +import zmaster587.libVulpes.LibVulpes; +import zmaster587.libVulpes.gui.CommonResources; +import zmaster587.libVulpes.inventory.GuiHandler; +import zmaster587.libVulpes.inventory.modules.*; +import zmaster587.libVulpes.network.PacketHandler; +import zmaster587.libVulpes.network.PacketMachine; +import zmaster587.libVulpes.tile.multiblock.TileMultiPowerConsumer; +import zmaster587.libVulpes.util.EmbeddedInventory; +import zmaster587.libVulpes.util.IconResource; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + + +/** + * Orbital Registry: satellites + stations + * + * Two tabs: + * 0 - Satellites + * 1 - Stations + * + * Both tabs: + * - Left window: scrollable list of objects (buttons) + * - Right window: detail view for the selected object + * - Slot 0: input chip (sat or station chip) + * - Slot 1: output written chip + * - "Scan" button to populate/refresh the list from server state + */ +public class TileOrbitalRegistry extends TileMultiPowerConsumer + implements IModularInventory, IButtonInventory, IGuiCallback, IInventory { + + // Simple 1x1 structure + public static final Object[][][] structure = new Object[][][] { + { { 'c' } } + }; + + // Inventory slots + private static final int SLOT_CHIP_IN = 0; + private static final int SLOT_CHIP_OUT = 1; + + // Tabs + private static final int TAB_SATELLITES = 0; + private static final int TAB_STATIONS = 1; + + // Left list: only slightly wider + private static final int OBS_LIST_BASE_X = 5; + private static final int OBS_LIST_BASE_Y = 32; + private static final int OBS_LIST_SIZE_X = 120; + private static final int OBS_LIST_SIZE_Y = 46; + + // Keep a small gap, give the rest of the width to the detail pane + private static final int OBS_DETAIL_BASE_X = 135; + private static final int OBS_DETAIL_BASE_Y = 32; + private static final int OBS_DETAIL_SIZE_X = 110; + private static final int OBS_DETAIL_SIZE_Y = 46; + + // Chip IO area (same as Observatory asteroid tab) + private static final int OBS_CHIP_X = 5; + private static final int OBS_CHIP_Y = 120; + + // GUI button IDs (client-side) + private static final int GUI_BUTTON_WRITE = 0; + private static final int GUI_BUTTON_SCAN = 1; + + // GUI list offsets (client-side) + private static final short SAT_LIST_OFFSET = 100; + private static final short STATION_LIST_OFFSET = 200; + + // Network IDs (PacketMachine) + private static final byte NET_TAB_SWITCH = 10; + private static final byte NET_BUTTON_SELECT_SAT = 11; + private static final byte NET_BUTTON_WRITE_CHIP = 12; + private static final byte NET_BUTTON_SCAN = 13; + private static final byte NET_BUTTON_SELECT_STAT = 14; + private static final byte NET_REQUEST_REOPEN = 15; + + // Synced “version” that changes whenever scan results change + private int scanNonce = 0; + // Client-only flag + private boolean pendingReopenAfterScan = false; + // Inventory + private final EmbeddedInventory inv; + + // Dimension whose satellites we show (usually effective dim of this tile) + private int satDimId = 0; + + // Selection / last pressed list button + private int lastSatButton = -1; + private long selectedSatId = -1L; + + private int lastStationButton = -1; + private int selectedStationId = -1; + + // Cached scan results (server-side authoritative, synced to client) + private final List satCache = new ArrayList<>(); + private final List stationCache = new ArrayList<>(); + + // Tab module (0 = satellites, 1 = stations) + private final ModuleTab tabModule; + + private static final String CHIP_PLANET_NAME_KEY = "name"; + + private static class SatEntry { + long id; + int dimId; + String registryKey; // satellite type / registry id / dataType + int powerGen; + int powerStorage; + long maxData; + boolean generatesData; + } + + private static class StationEntry { + int id; + int dimId; + int orbitingBodyId; + boolean anchored; + boolean hasWarpCore; + int freePads; + } + + public TileOrbitalRegistry() { + this.inv = new EmbeddedInventory(2); + this.powerPerTick = 0; // mostly passive + this.completionTime = 0; + + this.tabModule = new ModuleTab( + 4, 0, 0, + this, + 2, + new String[] { + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.tab.satellites"), + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.tab.stations") + }, + new net.minecraft.util.ResourceLocation[][] { + TextureResources.tabData, + TextureResources.tabAsteroid + } + ); + } + + + private void stampChipPlanetInfo(@Nonnull ItemStack stack, @Nonnull SatelliteBase sat) { + if (stack.isEmpty()) return; + + NBTTagCompound nbt = stack.getTagCompound(); + if (nbt == null) nbt = new NBTTagCompound(); + + int dimId = sat.getDimensionId(); + nbt.setInteger("dimId", dimId); + + DimensionProperties props = + zmaster587.advancedRocketry.dimension.DimensionManager.getInstance().getDimensionProperties(dimId); + if (props != null) { + nbt.setString(CHIP_PLANET_NAME_KEY, props.getName()); + } + + stack.setTagCompound(nbt); + } + /* ------------------------------------------------------------------------ + * Multiblock basics + * --------------------------------------------------------------------- */ + + @Override + public Object[][][] getStructure() { + return structure; + } + + @Override + public boolean completeStructure(net.minecraft.block.state.IBlockState state) { + boolean result = super.completeStructure(state); + ((zmaster587.libVulpes.block.multiblock.BlockMultiblockMachine) + world.getBlockState(pos).getBlock()) + .setBlockState(world, world.getBlockState(pos), pos, result); + return result; + } + + @Override + public String getMachineName() { + return LibVulpes.proxy.getLocalizedString("tile.orbitalRegistry.name"); + } + + /* ------------------------------------------------------------------------ + * Helpers / scans + * --------------------------------------------------------------------- */ + private static int calcCollectionTimeTicks(int powerGeneration) { + if (powerGeneration <= 0) return 0; + int ct = (int) (200.0 / Math.sqrt(0.1 * (double) powerGeneration)); + return (ct == 0) ? 200 : ct; + } + + private static double calcDataPerSecond(int powerGeneration) { + int ct = calcCollectionTimeTicks(powerGeneration); + if (ct <= 0) return 0.0; + return 20.0 / (double) ct; + } + private int getEffectiveSatDim() { + if (world == null) return satDimId; + + int eff = DimensionManager.getEffectiveDimId(world, pos).getId(); + satDimId = eff; + return eff; + } + + private int peekEffectiveSatDimForDisplay() { + if (world == null) return satDimId; + return DimensionManager.getEffectiveDimId(world, pos).getId(); + } + // Blacklist for "satellites" that should not appear in the orbital registry + private static final java.util.Set SAT_BLACKLIST = + java.util.Collections.unmodifiableSet( + new java.util.HashSet<>(java.util.Arrays.asList( + "asteroidMiner", + "gasMining" + )) + ); + + + /** + * Build satellite cache from DimensionProperties. + * Only called when Scan is pressed on the satellites tab. + */ + + private void rescanSatellites() { + satCache.clear(); + + int dimId = getEffectiveSatDim(); + DimensionProperties props = DimensionManager.getInstance().getDimensionProperties(dimId); + if (props == null) { + selectedSatId = -1L; + lastSatButton = -1; + return; + } + + java.util.Collection raw = props.getAllSatellites(); + if (raw == null) raw = java.util.Collections.emptyList(); + + List sats = new ArrayList<>(raw); + sats.sort(Comparator.comparingLong(SatelliteBase::getId)); + + for (SatelliteBase sat : sats) { + SatEntry entry = new SatEntry(); + entry.id = sat.getId(); + entry.dimId = sat.getDimensionId(); + + entry.registryKey = ""; + entry.powerGen = 0; + entry.powerStorage = 0; + entry.maxData = 0; + entry.generatesData = (sat instanceof SatelliteData); + try { + zmaster587.advancedRocketry.api.satellite.SatelliteProperties sProps = sat.getProperties(); + if (sProps != null) { + String type = sProps.getSatelliteType(); + if (type != null && !type.isEmpty()) { + entry.registryKey = type; + } + entry.powerGen = sProps.getPowerGeneration(); + entry.powerStorage = sProps.getPowerStorage(); + entry.maxData = sProps.getMaxDataStorage(); + } + } catch (Throwable ignored) {} + + // Fallback: derive registry key from the satellite class if properties didn't give one + if (entry.registryKey == null || entry.registryKey.isEmpty()) { + try { + String key = SatelliteRegistry.getKey(sat.getClass()); + if (key != null && !"poo".equals(key)) { // "poo" is the error sentinel in SatelliteRegistry + entry.registryKey = key; + } + } catch (Throwable ignored) {} + } + + // Absolute last-resort fallback so we always have "something" + if (entry.registryKey == null || entry.registryKey.isEmpty()) { + entry.registryKey = sat.getClass().getSimpleName().toLowerCase(); + } + + if (SAT_BLACKLIST.contains(entry.registryKey)) { + continue; + } + /* UNCOMMENT TO EXCLUDE MISSIONS FROM ORBITAL REGISTRY + ,but BLACKLIST OVER SHOULD HOLD FOR NOW + if (sat instanceof zmaster587.advancedRocketry.api.IMission) { + continue; + } + */ + + satCache.add(entry); + } + + // Keep selection if possible + long prevSelected = selectedSatId; + selectedSatId = -1L; + lastSatButton = -1; + + if (prevSelected >= 0L) { + for (int i = 0; i < satCache.size(); i++) { + if (satCache.get(i).id == prevSelected) { + selectedSatId = prevSelected; + lastSatButton = SAT_LIST_OFFSET + i; + break; + } + } + } + } + + /** + * Build station cache from SpaceObjectManager. + * Only called when Scan is pressed on the stations tab. + * + * NOTE: This assumes SpaceObjectManager exposes a way to iterate space objects. + * If your API is different (e.g. getSpaceStations(), getSpaceObjects()), + * adjust the iteration inside this method. + */ + + private void rescanStations() { + stationCache.clear(); + + final SpaceObjectManager manager = SpaceObjectManager.getSpaceManager(); + if (manager == null) { + selectedStationId = -1; + lastStationButton = -1; + return; + } + + final Iterable objects = manager.getSpaceObjects(); + if (objects == null) { + selectedStationId = -1; + lastStationButton = -1; + return; + } + + for (ISpaceObject obj : objects) { + if (obj == null) continue; // ultra-defensive, cheap + + StationEntry entry = new StationEntry(); + entry.id = obj.getId(); + entry.orbitingBodyId = obj.getOrbitingPlanetId(); + entry.anchored = obj.isAnchored(); + + entry.dimId = -1; + entry.hasWarpCore = false; + entry.freePads = 0; + + if (entry.orbitingBodyId == zmaster587.advancedRocketry.api.Constants.INVALID_PLANET + || entry.orbitingBodyId == SpaceObjectManager.WARPDIMID) { + entry.dimId = -1; + } else { + entry.dimId = entry.orbitingBodyId; + } + + if (obj instanceof SpaceStationObject) { + SpaceStationObject station = (SpaceStationObject) obj; + entry.hasWarpCore = station.hasWarpCores; + + int free = 0; + for (StationLandingLocation pad : station.getLandingPads()) { + if (!pad.getOccupied()) { + free++; + } + } + entry.freePads = free; + } + + stationCache.add(entry); + } + + stationCache.sort(Comparator.comparingInt(e -> e.id)); + + int prevSelected = selectedStationId; + selectedStationId = -1; + lastStationButton = -1; + + if (prevSelected >= 0) { + for (int i = 0; i < stationCache.size(); i++) { + if (stationCache.get(i).id == prevSelected) { + selectedStationId = prevSelected; + lastStationButton = STATION_LIST_OFFSET + i; + break; + } + } + } + } + + + private void handleSatelliteSelectionFromButton(int buttonId) { + lastSatButton = buttonId; + int idx = lastSatButton - SAT_LIST_OFFSET; + + if (idx >= 0 && idx < satCache.size()) { + selectedSatId = satCache.get(idx).id; + } else { + selectedSatId = -1L; + } + } + + private void handleStationSelectionFromButton(int buttonId) { + lastStationButton = buttonId; + int idx = lastStationButton - STATION_LIST_OFFSET; + + if (idx >= 0 && idx < stationCache.size()) { + selectedStationId = stationCache.get(idx).id; + } else { + selectedStationId = -1; + } + } + + /** + * Returns a localized display name for a satellite based on its raw name. + * + * - Builds a lang key of the form: + * msg.orbitalregistry.sat.name. + * - If no translation exists (returned string == key), falls back to the raw name. + * - If name is null/empty, uses a generic "unnamed" key. + */ + + private String getLocalizedSatName(SatEntry sat) { + String baseKey = sat.registryKey; + if (baseKey == null || baseKey.isEmpty()) { + baseKey = "unknown"; + } + + // Lang key you will define in the lang file: + // msg.orbitalregistry.sat.name. + String langKey = "msg.orbitalregistry.sat.name." + baseKey; + String localized = LibVulpes.proxy.getLocalizedString(langKey); + + // If missing, LibVulpes usually returns the key itself → then we just show the registryKey as text. + if (localized == null || localized.isEmpty() || localized.equals(langKey)) { + return baseKey; + } + return localized; + } + + private static class WriteCheck { + final boolean ok; + final String tooltipKey; + + WriteCheck(boolean ok, String tooltipKey) { + this.ok = ok; + this.tooltipKey = tooltipKey; + } + + static WriteCheck fail(String key) { + return new WriteCheck(false, key); + } + + static WriteCheck ok(String key) { + return new WriteCheck(true, key); + } + } + + private WriteCheck checkWrite() { + int tab = tabModule.getTab(); + + ItemStack in = getStackInSlot(SLOT_CHIP_IN); + ItemStack out = getStackInSlot(SLOT_CHIP_OUT); + + // Output blocking reason is universal + if (!out.isEmpty()) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.output"); + } + + // No input: avoid negative phrasing + if (in.isEmpty() || in.getCount() != 1) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.insert"); + } + + // SATELLITES TAB + if (tab == TAB_SATELLITES) { + + // maybe: + // if (satCache.isEmpty()) { + // return WriteCheck.fail("msg.orbitalregistry.writechip.hint.scan"); + // } + + if (selectedSatId < 0L) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.select"); + } + + SatelliteBase sat = DimensionManager.getInstance().getSatellite(selectedSatId); + if (sat == null) { + // Optional: could be hint.scan instead of select + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.select"); + } + + boolean isIdChip = in.getItem() instanceof ItemSatelliteIdentificationChip; + boolean isOreScanner = in.getItem() instanceof ItemOreScanner; + + if (!isIdChip && !isOreScanner) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.sat.or.idchip"); + } + + if (isOreScanner) { + if (!(sat instanceof zmaster587.advancedRocketry.satellite.SatelliteOreMapping)) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.sat.orescanner.only"); + } + return WriteCheck.ok("msg.orbitalregistry.writechip.ok"); + } + + // ID-chip path + if (!sat.isAcceptableControllerItemStack(in)) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.sat.badcontroller"); + } + + return WriteCheck.ok("msg.orbitalregistry.writechip.ok"); + } + + // STATIONS TAB + + // maybe: + // if (stationCache.isEmpty()) { + // return WriteCheck.fail("msg.orbitalregistry.writechip.hint.scan"); + // } + + if (!(in.getItem() instanceof ItemStationChip)) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.sat.or.stationchip"); + } + + if (selectedStationId < 0) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.select"); + } + + StationEntry selected = null; + for (StationEntry e : stationCache) { + if (e.id == selectedStationId) { + selected = e; + break; + } + } + + if (selected == null) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.select"); + } + + if (selected.orbitingBodyId == zmaster587.advancedRocketry.api.Constants.INVALID_PLANET) { + return WriteCheck.fail("msg.orbitalregistry.writechip.hint.station.unlaunched"); + } + + return WriteCheck.ok("msg.orbitalregistry.writechip.ok"); + } + + + /* ------------------------------------------------------------------------ + * Chip writing + * --------------------------------------------------------------------- */ + + + private void writeSatelliteChipFromSelection() { + if (world == null || world.isRemote) return; + + SatelliteBase sat = DimensionManager.getInstance().getSatellite(selectedSatId); + if (sat == null) return; + + ItemStack source = decrStackSize(SLOT_CHIP_IN, 1); + if (source.isEmpty()) return; + + if (source.getItem() instanceof ItemOreScanner) { + ItemOreScanner scanner = (ItemOreScanner) source.getItem(); + + if (!(sat instanceof zmaster587.advancedRocketry.satellite.SatelliteOreMapping)) { + setInventorySlotContents(SLOT_CHIP_IN, source); + return; + } + + scanner.setSatelliteID(source, selectedSatId); + stampChipPlanetInfo(source, sat); + setInventorySlotContents(SLOT_CHIP_OUT, source); + + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); + return; + } + + SatelliteProperties props = sat.getProperties(); + if (props == null) { + setInventorySlotContents(SLOT_CHIP_IN, source); + return; + } + + ItemStack programmed = sat.getControllerItemStack(source, props); + stampChipPlanetInfo(programmed, sat); + setInventorySlotContents(SLOT_CHIP_OUT, programmed); + + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); + } + + private boolean canWriteChipForCurrentTab() { + return checkWrite().ok; + } + + private void writeChipForCurrentTab() { + if (!checkWrite().ok) return; + + if (tabModule.getTab() == TAB_SATELLITES) { + writeSatelliteChipFromSelection(); + } else { + writeStationChipFromSelection(); + } + } + + /** + * Writes a station chip for the selected space station. + */ + private void writeStationChipFromSelection() { + if (world == null || world.isRemote) return; + + ItemStack sourceChip = decrStackSize(SLOT_CHIP_IN, 1); + if (sourceChip.isEmpty() || !(sourceChip.getItem() instanceof ItemStationChip)) { + return; + } + + ItemStationChip chipItem = (ItemStationChip) sourceChip.getItem(); + chipItem.setUUID(sourceChip, selectedStationId); + + setInventorySlotContents(SLOT_CHIP_OUT, sourceChip); + + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); + } + + /* ------------------------------------------------------------------------ + * Core tick / processing + * --------------------------------------------------------------------- */ + + @Override + protected void processComplete() { + } + + @Override + public boolean isRunning() { + return false; + } + + /* ------------------------------------------------------------------------ + * GUI modules + * --------------------------------------------------------------------- */ + + @Override + public List getModules(int ID, EntityPlayer player) { + List modules = new LinkedList<>(); + + // --- Extra right-hand backdrop: stretch main GUI full height with 3 slices --- + if (world != null && world.isRemote) { + + final int extX = 173; + final int guiTopY = 0; + final int guiBottomY = 168; + final int extWidth = 78; + + // TOP: the 86px slice that already lined up with the main GUI top + modules.add(new ModuleImage( + extX, guiTopY, + new IconResource( + 98, 0, + extWidth, 86, + CommonResources.genericBackground + ) + )); + + // MIDDLE: fill from y=86 down to y=168, + modules.add(new ModuleImage( + extX, guiTopY + 86, + new IconResource( + 98, 3, + extWidth, guiBottomY - 86, + CommonResources.genericBackground + ) + )); + + // BOTTOM: the 3px strip that already lined up with the main GUI bottom + modules.add(new ModuleImage( + extX, guiBottomY, + new IconResource( + 98, 168, + extWidth, 3, + CommonResources.genericBackground + ) + )); + } + + modules.add(tabModule); + //no powerbar + //modules.add(new ModulePower(18, 20, getBatteries())); + + final int tab = tabModule.getTab(); + + // ----- CHIP IO + BUTTONS (bottom, same as Observatory) ----- + // Same layout as TileObservatory asteroid tab: (5,120) / (45,120) / 25 / 100 + modules.add(new ModuleTexturedSlotArray( + OBS_CHIP_X, OBS_CHIP_Y, + this, + SLOT_CHIP_IN, SLOT_CHIP_IN + 1, + TextureResources.idChip)); + + modules.add(new ModuleOutputSlotArray( + OBS_CHIP_X + 40, OBS_CHIP_Y, + this, + SLOT_CHIP_OUT, SLOT_CHIP_OUT + 1)); + + ModuleButton scanBtn = new ModuleButton( + 110, OBS_CHIP_Y, + GUI_BUTTON_SCAN, + LibVulpes.proxy.getLocalizedString("msg.observetory.scan.button"), + this, + zmaster587.libVulpes.inventory.TextureResources.buttonBuild, + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.scan.tooltip"), + 64, 18 + ); + modules.add(scanBtn); + + // Progress bar + modules.add(new ModuleProgress( + OBS_CHIP_X + 20, OBS_CHIP_Y, + 0, + new ProgressBarImage( + 217, 0, 17, 17, + 234, 0, + EnumFacing.DOWN, + TextureResources.progressBars + ), + this + )); + + ModuleButton writeBtn = new ModuleButton( + OBS_CHIP_X + 20, OBS_CHIP_Y, + GUI_BUTTON_WRITE, + "", + this, + zmaster587.libVulpes.inventory.TextureResources.buttonNull, + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.writechip"), + 17, 17 + ); + WriteCheck wc = checkWrite(); + writeBtn.setToolTipText(LibVulpes.proxy.getLocalizedString(wc.tooltipKey)); + + modules.add(writeBtn); + + // ----- WINDOWS (left list + right detail) ----- + final int listBaseX = OBS_LIST_BASE_X; + final int listBaseY = OBS_LIST_BASE_Y; + final int listSizeX = OBS_LIST_SIZE_X; + final int listSizeY = OBS_LIST_SIZE_Y; + + final int detailBaseX = OBS_DETAIL_BASE_X; + final int detailBaseY = OBS_DETAIL_BASE_Y; + final int detailSizeX = OBS_DETAIL_SIZE_X; + final int detailSizeY = OBS_DETAIL_SIZE_Y; + + if (world != null && world.isRemote) { + // Left window frame + modules.add(new ModuleScaledImage( + listBaseX - 3, listBaseY - 3, + 3, listBaseY + listSizeY + 6, + TextureResources.verticalBar)); + modules.add(new ModuleScaledImage( + listBaseX + listSizeX, listBaseY - 3, + -3, listBaseY + listSizeY + 6, + TextureResources.verticalBar)); + modules.add(new ModuleScaledImage( + listBaseX, listBaseY - 3, + listSizeX, 3, + TextureResources.horizontalBar)); + modules.add(new ModuleScaledImage( + listBaseX, 2 * listBaseY + listSizeY, + listSizeX, -3, + TextureResources.horizontalBar)); + + // Right window frame + modules.add(new ModuleScaledImage( + detailBaseX - 3, detailBaseY - 3, + 3, detailBaseY + detailSizeY + 6, + TextureResources.verticalBar)); + modules.add(new ModuleScaledImage( + detailBaseX + detailSizeX, detailBaseY - 3, + -3, detailBaseY + detailSizeY + 6, + TextureResources.verticalBar)); + modules.add(new ModuleScaledImage( + detailBaseX, detailBaseY - 3, + detailSizeX, 3, + TextureResources.horizontalBar)); + modules.add(new ModuleScaledImage( + detailBaseX, 2 * detailBaseY + detailSizeY, + detailSizeX, -3, + TextureResources.horizontalBar)); + } + + // Title positions + if (tab == TAB_SATELLITES) { + modules.add(new ModuleText( + 10, 18, + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.satellites"), + 0x2d2d2d + )); + String detailsLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.details"); + String dimLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.dimid"); + String detailsTitle = detailsLabel + " " + dimLabel + " " + peekEffectiveSatDimForDisplay(); + + modules.add(new ModuleText( + OBS_DETAIL_BASE_X - 5, + 18, + detailsTitle, + 0x2d2d2d + )); + + buildSatelliteListWindow(modules, listBaseX, listBaseY, listSizeX, listSizeY); + buildSatelliteDetailWindow(modules, detailBaseX + 3, detailBaseY + 3); + + } else { + modules.add(new ModuleText( + 10, 18, + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.stations"), + 0x2d2d2d + )); + modules.add(new ModuleText( + OBS_DETAIL_BASE_X - 5, + 18, + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.details"), + 0x2d2d2d + )); + + buildStationListWindow(modules, listBaseX, listBaseY, listSizeX, listSizeY); + buildStationDetailWindow(modules, detailBaseX + 3, detailBaseY + 3); + } + + return modules; + } + + private void buildSatelliteListWindow(List modules, + int baseX, int baseY, int sizeX, int sizeY) { + + List satButtons = new LinkedList<>(); + + for (int i = 0; i < satCache.size(); i++) { + SatEntry sat = satCache.get(i); + + int buttonId = SAT_LIST_OFFSET + i; + String displayName = getLocalizedSatName(sat); + String label = String.format("ID %d %s", sat.id, displayName); + + ModuleButton button = new ModuleButton( + 0, + i * 18, + buttonId, + label, + this, + TextureResources.buttonAsteroid, + OBS_LIST_SIZE_X, 18 + ); + + if (sat.id == selectedSatId) { + button.setColor(0xFFFF00); + } + + satButtons.add(button); + } + + if (!satButtons.isEmpty()) { + modules.add(AdvancedRocketry.proxy.createScrollListPan( + baseX, baseY, + satButtons, + sizeX, sizeY + )); + } + } + + private void buildSatelliteDetailWindow(List modules, int startX, int startY) { + int x = startX; + int y = startY; + + if (selectedSatId < 0L || satCache.isEmpty()) { + modules.add(new ModuleText( + x, y, + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.nosel"), + 0xAA0000 + )); + return; + } + + SatEntry selected = null; + for (SatEntry e : satCache) { + if (e.id == selectedSatId) { + selected = e; + break; + } + } + + if (selected == null) { + modules.add(new ModuleText( + x, y, + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.notfound"), + 0xAA0000 + )); + return; + } + + // ----- ID: ----- + String idLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.id"); // "ID:" + String idLine = idLabel + " " + selected.id; + modules.add(new ModuleText(x, y, idLine, 0x2d2d2d)); + y += 10; + + // ----- Type: localized satellite name (from registryKey) ----- + String typeLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.type"); // "Type:" + String typeLine = typeLabel + " " + getLocalizedSatName(selected); + modules.add(new ModuleText(x, y, typeLine, 0x2d2d2d)); + y += 10; + + /* Moved to header for now + // ----- Dim: ----- + String dimLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.dimid"); // "Dim:" + String dimLine = dimLabel + " " + selected.dimId; + modules.add(new ModuleText(x, y, dimLine, 0x2d2d2d)); + y += 10; + */ + + // ----- Orbiting: ----- + String orbitLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.orbit"); // "Orbiting:" + + String orbitName; + DimensionProperties bodyProps = + DimensionManager.getInstance().getDimensionProperties(selected.dimId); + if (bodyProps != null) { + orbitName = bodyProps.getName(); + } else { + orbitName = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.dimid.none"); + } + + String orbitLine = orbitLabel + " " + orbitName; + modules.add(new ModuleText(x, y, orbitLine, 0x2d2d2d)); + y += 10; + + // ----- Power + data ----- + if (selected.powerGen != 0 || selected.powerStorage != 0 || selected.maxData != 0) { + String pwrGenLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.sat.pwrgen"); + String pwrStoreLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.sat.pwrstore"); + String maxDataLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.sat.maxdata"); + + modules.add(new ModuleText(x, y, pwrGenLabel + " " + selected.powerGen, 0x2d2d2d)); + y += 10; + modules.add(new ModuleText(x, y, pwrStoreLabel + " " + selected.powerStorage, 0x2d2d2d)); + y += 10; + modules.add(new ModuleText(x, y, maxDataLabel + " " + selected.maxData, 0x2d2d2d)); + y += 10; + } + // ----- Data gen: ----- (only if meaningful) + if (selected.generatesData && selected.powerGen > 0 && selected.maxData > 0) { + double dps = calcDataPerSecond(selected.powerGen); + String dpsStr = String.format(java.util.Locale.ROOT, "%.3f", dps); + + String prefix = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.sat.datagen"); + if (prefix == null || prefix.isEmpty() || prefix.equals("msg.orbitalregistry.text.sat.datagen")) { + prefix = "Data gen:"; + } + + modules.add(new ModuleText(x, y, prefix + " " + dpsStr + "/s", 0x2d2d2d)); + y += 10; + } +} + + + + private void buildStationListWindow(List modules, + int baseX, int baseY, int sizeX, int sizeY) { + + List stationButtons = new LinkedList<>(); + + for (int i = 0; i < stationCache.size(); i++) { + StationEntry st = stationCache.get(i); + + int buttonId = STATION_LIST_OFFSET + i; + + // Short type text (localized) + String typeShort = st.hasWarpCore + ? LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.type.starshiplist") + : LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.type.station"); + + // "ID" label from lang, then "ID " + String listPrefix = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.listentry"); + String label = listPrefix + " " + st.id + " " + typeShort; + + + ModuleButton button = new ModuleButton( + 0, + i * 18, + buttonId, + label, + this, + TextureResources.buttonAsteroid, + OBS_LIST_SIZE_X, 18 + ); + + if (st.id == selectedStationId) { + button.setColor(0xFFFF00); + } + + stationButtons.add(button); + } + + if (!stationButtons.isEmpty()) { + modules.add(AdvancedRocketry.proxy.createScrollListPan( + baseX, baseY, + stationButtons, + sizeX, sizeY + )); + } + } + + private void buildStationDetailWindow(List modules, int startX, int startY) { + int x = startX; + int y = startY; + + if (selectedStationId < 0 || stationCache.isEmpty()) { + modules.add(new ModuleText( + x, y, + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.nosel"), + 0xAA0000 + )); + return; + } + + StationEntry selected = null; + for (StationEntry e : stationCache) { + if (e.id == selectedStationId) { + selected = e; + break; + } + } + + if (selected == null) { + modules.add(new ModuleText( + x, y, + LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.notfound"), + 0xAA0000 + )); + return; + } + + // ----- ID: ----- + String idLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.id"); // e.g. "ID:" + String idLine = idLabel + " " + selected.id; + modules.add(new ModuleText(x, y, idLine, 0x2d2d2d)); + y += 10; + + // ----- Type: ----- + String typeLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.type"); // e.g. "Type:" + String typeKey = selected.hasWarpCore + ? LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.type.starship") + : LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.type.station"); + String typeLine = typeLabel + " " + typeKey; + modules.add(new ModuleText(x, y, typeLine, 0x2d2d2d)); + y += 10; + + // ----- DimID: ----- + String dimText; + if (selected.orbitingBodyId == zmaster587.advancedRocketry.api.Constants.INVALID_PLANET + || selected.orbitingBodyId == SpaceObjectManager.WARPDIMID) { + // No real body below → None + dimText = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.dimid.none"); + } else { + dimText = Integer.toString(selected.dimId); + } + String dimLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.dimid"); // e.g. "DimID:" + String dimLine = dimLabel + " " + dimText; + modules.add(new ModuleText(x, y, dimLine, 0x2d2d2d)); + y += 10; + + // ----- Orbiting: ----- + String orbitName; + String systemName; + + if (selected.orbitingBodyId == zmaster587.advancedRocketry.api.Constants.INVALID_PLANET + || selected.orbitingBodyId == SpaceObjectManager.WARPDIMID) { + // Treat as unlaunched / no system + orbitName = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.orbit.unlaunched"); + systemName = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.system.none"); // e.g. "None" + } else { + DimensionProperties bodyProps = + zmaster587.advancedRocketry.dimension.DimensionManager + .getInstance().getDimensionProperties(selected.orbitingBodyId); + + if (bodyProps != null) { + orbitName = bodyProps.getName(); + + // Try to get star/system name + if (bodyProps.getStar() != null && bodyProps.getStar().getName() != null) { + systemName = bodyProps.getStar().getName(); + } else { + systemName = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.system.unknown"); + } + } else { + // Fallback: raw ID, unknown system + orbitName = Integer.toString(selected.orbitingBodyId); + systemName = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.system.unknown"); + } + } + + String orbitLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.orbit"); // "Orbiting:" + String orbitLine = orbitLabel + " " + orbitName; + modules.add(new ModuleText(x, y, orbitLine, 0x2d2d2d)); + y += 10; + + // ----- System: ----- (NEW) + String systemLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.system"); // "System:" + String systemLine = systemLabel + " " + systemName; + modules.add(new ModuleText(x, y, systemLine, 0x2d2d2d)); + y += 10; + + + // ----- Free landingpads: <#freepads> ----- + String padsLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.freepads"); // "Free landingpads:" + String padsLine = padsLabel + " " + selected.freePads; + modules.add(new ModuleText(x, y, padsLine, 0x2d2d2d)); + y += 10; + + // ----- Anchored: yes/no ----- + String anchoredLabel = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.anchored"); // "Anchored:" + String anchoredYes = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.anchored.yes"); + String anchoredNo = LibVulpes.proxy.getLocalizedString("msg.orbitalregistry.text.anchored.no"); + String anchoredVal = selected.anchored ? anchoredYes : anchoredNo; + String anchoredLine = anchoredLabel + " " + anchoredVal; + modules.add(new ModuleText(x, y, anchoredLine, 0x2d2d2d)); + y += 10; + } + + /* ------------------------------------------------------------------------ + * Button handling + * --------------------------------------------------------------------- */ + + @Override + public void onInventoryButtonPressed(int buttonId) { + // Client → server via PacketMachine + if (world != null && world.isRemote) { + if (buttonId == GUI_BUTTON_SCAN) { + AdvancedRocketry.proxy.clearScrollCache(); + pendingReopenAfterScan = true; + PacketHandler.sendToServer(new PacketMachine(this, NET_BUTTON_SCAN)); + return; + } + + if (buttonId == GUI_BUTTON_WRITE) { + PacketHandler.sendToServer(new PacketMachine(this, NET_BUTTON_WRITE_CHIP)); + return; + } else if (buttonId >= SAT_LIST_OFFSET && buttonId < STATION_LIST_OFFSET) { + // NEW: update client-side selection immediately + lastSatButton = buttonId; + handleSatelliteSelectionFromButton(buttonId); + + PacketHandler.sendToServer(new PacketMachine(this, NET_BUTTON_SELECT_SAT)); + + } else if (buttonId >= STATION_LIST_OFFSET) { + // NEW: update client-side selection immediately + lastStationButton = buttonId; + handleStationSelectionFromButton(buttonId); + + PacketHandler.sendToServer(new PacketMachine(this, NET_BUTTON_SELECT_STAT)); + } + return; + } + + // Server-side fallback (normally PacketMachine + useNetworkData) + if (buttonId == GUI_BUTTON_WRITE) { + writeChipForCurrentTab(); + } else if (buttonId == GUI_BUTTON_SCAN) { + if (tabModule.getTab() == TAB_SATELLITES) { + rescanSatellites(); + } else { + rescanStations(); + } + markDirty(); + } else if (buttonId >= SAT_LIST_OFFSET && buttonId < STATION_LIST_OFFSET) { + handleSatelliteSelectionFromButton(buttonId); + } else if (buttonId >= STATION_LIST_OFFSET) { + handleStationSelectionFromButton(buttonId); + } + } + + + @Override + public void onModuleUpdated(ModuleBase module) { + // Tab switched; tell server to update and reopen GUI + PacketHandler.sendToServer(new PacketMachine(this, NET_TAB_SWITCH)); + } + + /* ------------------------------------------------------------------------ + * INetworkMachine: custom packets + * --------------------------------------------------------------------- */ + + @Override + public void writeDataToNetwork(ByteBuf out, byte id) { + super.writeDataToNetwork(out, id); + + if (id == NET_TAB_SWITCH) { + out.writeShort(tabModule.getTab()); + } else if (id == NET_BUTTON_SELECT_SAT) { + out.writeShort(lastSatButton); + } else if (id == NET_BUTTON_SELECT_STAT) { + out.writeShort(lastStationButton); + } + } + + @Override + public void readDataFromNetwork(ByteBuf in, byte packetId, NBTTagCompound nbt) { + super.readDataFromNetwork(in, packetId, nbt); + + if (packetId == NET_TAB_SWITCH) { + nbt.setShort("tab", in.readShort()); + } else if (packetId == NET_BUTTON_SELECT_SAT) { + nbt.setShort("buttonSat", in.readShort()); + } else if (packetId == NET_BUTTON_SELECT_STAT) { + nbt.setShort("buttonStation", in.readShort()); + } + } + + @Override + public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompound nbt) { + super.useNetworkData(player, side, id, nbt); + + if (!world.isRemote) { + if (id == NET_TAB_SWITCH) { + tabModule.setTab(nbt.getShort("tab")); + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); + + } else if (id == NET_BUTTON_SELECT_SAT) { + int btn = nbt.getShort("buttonSat"); + handleSatelliteSelectionFromButton(btn); + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); + + } else if (id == NET_BUTTON_SELECT_STAT) { + int btn = nbt.getShort("buttonStation"); + handleStationSelectionFromButton(btn); + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); + + } else if (id == NET_BUTTON_WRITE_CHIP) { + writeChipForCurrentTab(); + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); + + } else if (id == NET_BUTTON_SCAN) { + if (tabModule.getTab() == TAB_SATELLITES) { + rescanSatellites(); + } else { + rescanStations(); + } + scanNonce++; + selectedSatId = -1L; + lastSatButton = -1; + selectedStationId = -1; + lastStationButton = -1; + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); + //player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), getWorld(), pos.getX(), pos.getY(), pos.getZ()); + } else if (id == NET_REQUEST_REOPEN) { + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); + } + } + } + + /* ------------------------------------------------------------------------ + * Persistent state (world save + client sync) + * --------------------------------------------------------------------- */ + + @Override + protected void writeNetworkData(NBTTagCompound nbt) { + super.writeNetworkData(nbt); + writeCommonNBT(nbt); + } + + @Override + protected void readNetworkData(NBTTagCompound nbt) { + int prevNonce = this.scanNonce; + super.readNetworkData(nbt); + readCommonNBT(nbt); + + // Client: only reopen AFTER we have the new cache NBT + if (world != null && world.isRemote + && pendingReopenAfterScan + && prevNonce != this.scanNonce + && net.minecraft.client.Minecraft.getMinecraft().currentScreen instanceof zmaster587.libVulpes.inventory.GuiModular) { + pendingReopenAfterScan = false; + PacketHandler.sendToServer(new PacketMachine(this, NET_REQUEST_REOPEN)); + } + } + + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound nbt) { + super.writeToNBT(nbt); + inv.writeToNBT(nbt); + writeCommonNBT(nbt); + return nbt; + } + + @Override + public void readFromNBT(NBTTagCompound nbt) { + super.readFromNBT(nbt); + inv.readFromNBT(nbt); + readCommonNBT(nbt); + } + + private void writeCommonNBT(NBTTagCompound nbt) { + nbt.setInteger("satDimId", satDimId); + nbt.setInteger("lastSatButton", lastSatButton); + nbt.setLong("selectedSatId", selectedSatId); + nbt.setInteger("lastStationButton", lastStationButton); + nbt.setInteger("selectedStationId", selectedStationId); + nbt.setInteger("scanNonce", scanNonce); + // Satellite cache + NBTTagList satList = new NBTTagList(); + for (SatEntry e : satCache) { + NBTTagCompound tag = new NBTTagCompound(); + tag.setLong("id", e.id); + tag.setInteger("dimId", e.dimId); + tag.setString("registryKey", e.registryKey == null ? "" : e.registryKey); + tag.setInteger("powerGen", e.powerGen); + tag.setInteger("powerStorage", e.powerStorage); + tag.setLong("maxData", e.maxData); + tag.setBoolean("generatesData", e.generatesData); + satList.appendTag(tag); + } + nbt.setTag("satCache", satList); + + + + // Station cache + NBTTagList stationList = new NBTTagList(); + for (StationEntry e : stationCache) { + NBTTagCompound tag = new NBTTagCompound(); + tag.setInteger("id", e.id); + tag.setInteger("dimId", e.dimId); + tag.setInteger("orbitingBodyId", e.orbitingBodyId); + tag.setBoolean("anchored", e.anchored); + tag.setBoolean("hasWarpCore", e.hasWarpCore); + tag.setInteger("freePads", e.freePads); + stationList.appendTag(tag); + } + nbt.setTag("stationCache", stationList); + + } + + private void readCommonNBT(NBTTagCompound nbt) { + satDimId = nbt.getInteger("satDimId"); + lastSatButton = nbt.getInteger("lastSatButton"); + selectedSatId = nbt.getLong("selectedSatId"); + lastStationButton = nbt.getInteger("lastStationButton"); + selectedStationId = nbt.getInteger("selectedStationId"); + scanNonce = nbt.getInteger("scanNonce"); + satCache.clear(); + if (nbt.hasKey("satCache")) { + NBTTagList satList = nbt.getTagList("satCache", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < satList.tagCount(); i++) { + NBTTagCompound tag = satList.getCompoundTagAt(i); + SatEntry e = new SatEntry(); + e.id = tag.getLong("id"); + e.dimId = tag.getInteger("dimId"); + e.registryKey = tag.getString("registryKey"); + e.powerGen = tag.getInteger("powerGen"); + e.powerStorage= tag.getInteger("powerStorage"); + e.maxData = tag.getLong("maxData"); + e.generatesData = tag.getBoolean("generatesData"); + satCache.add(e); + } + } + + + stationCache.clear(); + if (nbt.hasKey("stationCache")) { + NBTTagList stationList = nbt.getTagList("stationCache", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < stationList.tagCount(); i++) { + NBTTagCompound tag = stationList.getCompoundTagAt(i); + StationEntry e = new StationEntry(); + e.id = tag.getInteger("id"); + e.dimId = tag.getInteger("dimId"); // NEW + e.orbitingBodyId = tag.getInteger("orbitingBodyId"); + e.anchored = tag.getBoolean("anchored"); + e.hasWarpCore = tag.getBoolean("hasWarpCore"); // NEW + e.freePads = tag.getInteger("freePads"); // NEW + stationCache.add(e); + } + } + } + + /* ------------------------------------------------------------------------ + * Inventory plumbing (2 slots) + * --------------------------------------------------------------------- */ + + @Override + public int getSizeInventory() { + return inv.getSizeInventory(); + } + + @Override + @Nonnull + public ItemStack getStackInSlot(int slot) { + return inv.getStackInSlot(slot); + } + + @Override + @Nonnull + public ItemStack decrStackSize(int slot, int amount) { + return inv.decrStackSize(slot, amount); + } + + @Override + public void setInventorySlotContents(int slot, @Nonnull ItemStack stack) { + inv.setInventorySlotContents(slot, stack); + } + + @Override + public boolean hasCustomName() { + return inv.hasCustomName(); + } + + @Override + public int getInventoryStackLimit() { + return 1; + } + + @Override + public boolean isUsableByPlayer(@Nullable EntityPlayer player) { + return player != null && player.getDistanceSq(pos) < 4096; + } + + @Override + public void openInventory(EntityPlayer player) { + inv.openInventory(player); + } + + @Override + public void closeInventory(EntityPlayer player) { + inv.closeInventory(player); + } + + @Override + public boolean isItemValidForSlot(int slot, @Nonnull ItemStack stack) { + if (slot == SLOT_CHIP_IN) { + if (stack.getCount() != 1) return false; + Item item = stack.getItem(); + + return (item instanceof ItemSatelliteIdentificationChip) + || (item instanceof ItemStationChip) + || (item instanceof ItemOreScanner); + } + return false; + } + + + @Override + @Nonnull + public ItemStack removeStackFromSlot(int index) { + return inv.removeStackFromSlot(index); + } + + @Override + public int getField(int id) { + return inv.getField(id); + } + + @Override + public void setField(int id, int value) { + inv.setField(id, value); + } + + @Override + public int getFieldCount() { + return inv.getFieldCount(); + } + + @Override + public void clear() { + inv.clear(); + } + + @Override + public boolean isEmpty() { + return inv.isEmpty(); + } + + @Override + @Nullable + public String getName() { + return null; + } + + @Override + public void invalidate() { + super.invalidate(); + + // Optional but nice to keep state sane + satCache.clear(); + stationCache.clear(); + selectedSatId = -1; + selectedStationId = -1; + lastSatButton = -1; + lastStationButton = -1; + + // Critical: reset static scroll cache so containers don't reuse old offsets + if (world != null && world.isRemote) { + AdvancedRocketry.proxy.clearScrollCache(); + } + + } + + @Override + public void onChunkUnload() { + super.onChunkUnload(); + if (world != null && world.isRemote) { + AdvancedRocketry.proxy.clearScrollCache(); + } + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/tile/TilePump.java b/src/main/java/zmaster587/advancedRocketry/tile/TilePump.java index 59c57298f..89efe5593 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/TilePump.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/TilePump.java @@ -1,8 +1,11 @@ package zmaster587.advancedRocketry.tile; import net.minecraft.block.Block; +import net.minecraft.block.BlockLiquid; import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -11,6 +14,7 @@ import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidTank; +import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.IFluidBlock; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; @@ -22,6 +26,9 @@ import zmaster587.libVulpes.network.PacketHandler; import zmaster587.libVulpes.tile.TileEntityRFConsumer; + + + import java.util.*; public class TilePump extends TileEntityRFConsumer implements IFluidHandler, IModularInventory { @@ -29,6 +36,8 @@ public class TilePump extends TileEntityRFConsumer implements IFluidHandler, IMo private final int RANGE = 64; private FluidTank tank; private List cache; + private Fluid lastFluidType = null; + private int localTick = 0; public TilePump() { super(1000); @@ -36,6 +45,16 @@ public TilePump() { cache = new LinkedList<>(); } + private static final int PUMP_INTERVAL_TICKS = 25; // ~1 Hz + private static final int EJECT_INTERVAL_TICKS = 20; // 1 Hz + private final IFluidHandler fluidCap = new FluidCapability(this); + + private boolean shouldRunThisTick(int interval) { + return interval <= 1 || (localTick % interval) == 0; + } + + + public int getPowerPerOperation() { return 100; } @@ -50,132 +69,258 @@ public boolean hasCapability(Capability capability, EnumFacing facing) { @Override public T getCapability(Capability capability, EnumFacing facing) { if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) { - return (T) new FluidCapability(this); + return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(fluidCap); } return super.getCapability(capability, facing); } @Override public void update() { + if (world == null || world.isRemote) return; + + localTick++; + if (localTick == Integer.MIN_VALUE) localTick = 0; + // Drop stale plan if accepted fluid changed + Fluid cur = tank.getFluid() == null ? null : tank.getFluid().getFluid(); + if (cur != lastFluidType) { + if (!cache.isEmpty()) cache.clear(); + lastFluidType = cur; + } + + if (isRedstoneDisabled()) { + return; + } + super.update(); - //Attempt fluid Eject - if (!world.isRemote && tank.getFluid() != null) { - for (EnumFacing direction : EnumFacing.values()) { - BlockPos newBlock = getPos().offset(direction); - TileEntity tile = world.getTileEntity(newBlock); - if (tile != null && tile.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, direction.getOpposite())) { - IFluidHandler cap = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, direction.getOpposite()); - FluidStack stack = tank.getFluid().copy(); - stack.amount = Math.min(tank.getFluid().amount, 1000); - //Perform the drain - cap.fill(tank.drain(cap.fill(stack, false), true), true); - - //Abort if we run out of fluid - if (tank.getFluid() == null) - break; + // Attempt fluid Eject (throttled; see section 3) + if (shouldRunThisTick(EJECT_INTERVAL_TICKS) && tank.getFluid() != null) { + final FluidStack src = tank.getFluid(); + final int toOffer = Math.min(src.amount, 1000); + if (toOffer > 0) { + for (EnumFacing dir : EnumFacing.values()) { + TileEntity te = world.getTileEntity(pos.offset(dir)); + if (te == null || !te.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, dir.getOpposite())) + continue; + + IFluidHandler out = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, dir.getOpposite()); + if (out == null) continue; + + int simAccepted = out.fill(new FluidStack(src, toOffer), false); + if (simAccepted > 0) { + FluidStack drained = tank.drain(simAccepted, true); + if (drained != null && drained.amount > 0) { + out.fill(drained, true); + if (tank.getFluid() == null) break; // ran out + } + } } } } } - private int getFrequencyFromPower() { - float ratio = energy.getUniversalEnergyStored() / (float) energy.getMaxEnergyStored(); - if (ratio > 0.5) - return 1; - return 10; + private boolean isVanillaLiquid(BlockPos pos) { + IBlockState state = world.getBlockState(pos); + Material mat = state.getMaterial(); + return mat == Material.WATER || mat == Material.LAVA; + } + + private boolean isVanillaSource(BlockPos pos) { + IBlockState state = world.getBlockState(pos); + Block block = state.getBlock(); + if (block instanceof BlockLiquid) { + // 0 == source, 1..7 flowing (or up to 15, depending) + Integer lvl = state.getValue(BlockLiquid.LEVEL); + return lvl != null && lvl == 0; + } + return false; + } + + private Fluid getVanillaFluid(BlockPos pos) { + Material mat = world.getBlockState(pos).getMaterial(); + if (mat == Material.WATER) return FluidRegistry.WATER; + if (mat == Material.LAVA) return FluidRegistry.LAVA; + return null; } + + @Override public void performFunction() { - if (!world.isRemote) { - //Do we have room? if (tank.getCapacity() - 1000 < tank.getFluidAmount()) return; BlockPos nextPos = getNextBlockLocation(); - if (nextPos != null) { - if (canFitFluid(nextPos)) { - Block worldBlock = world.getBlockState(nextPos).getBlock(); - Material mat = world.getBlockState(nextPos).getMaterial(); - if (worldBlock instanceof IFluidBlock) { - FluidStack fStack = ((IFluidBlock) worldBlock).drain(world, nextPos, true); - - if (fStack != null) - tank.fill(fStack, true); - int colour = ((IFluidBlock) worldBlock).getFluid().getColor(); - if (mat == Material.LAVA) - colour = 0xFFbd3718; - - PacketHandler.sendToNearby(new PacketFluidParticle(nextPos, this.pos, 200, colour), world.provider.getDimension(), this.pos, 128); + if (nextPos != null && canFitFluid(nextPos)) { + IBlockState state = world.getBlockState(nextPos); + Block worldBlock = state.getBlock(); + Material mat = state.getMaterial(); + + if (worldBlock instanceof IFluidBlock) { + FluidStack fStack = ((IFluidBlock) worldBlock).drain(world, nextPos, true); + if (fStack != null) tank.fill(fStack, true); + + int colour = ((IFluidBlock) worldBlock).getFluid().getColor(); + if (mat == Material.LAVA) colour = 0xFFbd3718; + PacketHandler.sendToNearby(new PacketFluidParticle(nextPos, this.pos, 200, colour), world.provider.getDimension(), this.pos, 128); + } else if (isVanillaLiquid(nextPos) && isVanillaSource(nextPos)) { + Fluid f = getVanillaFluid(nextPos); + if (f != null) { + FluidStack stack = new FluidStack(f, 1000); + int filled = tank.fill(stack, true); + if (filled == 1000) { + world.setBlockToAir(nextPos); // remove the source + int colour = (mat == Material.LAVA) ? 0xFFbd3718 : 0xFF3F76E4; // MC-ish tint + PacketHandler.sendToNearby(new PacketFluidParticle(nextPos, this.pos, 200, colour), world.provider.getDimension(), this.pos, 128); + } } } } } } + private boolean canFitFluid(BlockPos pos) { Block worldBlock = world.getBlockState(pos).getBlock(); if (worldBlock instanceof IFluidBlock) { - // Can we put it into the tank? return tank.getFluid() == null || tank.getFluid().getFluid() == ((IFluidBlock) worldBlock).getFluid(); } + if (isVanillaLiquid(pos)) { + Fluid f = getVanillaFluid(pos); + return f != null && (tank.getFluid() == null || tank.getFluid().getFluid() == f); + } return false; } - private BlockPos getNextBlockLocation() { + private BlockPos getNextBlockLocation() { if (!cache.isEmpty()) return cache.remove(0); - BlockPos currentPos = new MutableBlockPos(getPos().down()); + MutableBlockPos currentPos = new MutableBlockPos(pos); + currentPos.move(EnumFacing.DOWN); - while (world.isAirBlock(currentPos)) - currentPos = currentPos.down(); + while (currentPos.getY() > 0 && world.isAirBlock(currentPos)) { + currentPos.move(EnumFacing.DOWN); + } + if (currentPos.getY() <= 0) return null; // nothing below + if (!world.isBlockLoaded(currentPos)) return null; - // We found a fluid Block worldBlock = world.getBlockState(currentPos).getBlock(); - if (canFitFluid(currentPos)) - findFluidAtOrAbove(currentPos, ((IFluidBlock) worldBlock).getFluid()); + if (canFitFluid(currentPos)) { + Fluid target = null; + if (worldBlock instanceof IFluidBlock) { + target = ((IFluidBlock) worldBlock).getFluid(); + } else if (isVanillaLiquid(currentPos)) { + target = getVanillaFluid(currentPos); + } + findFluidAtOrAbove(currentPos, target); + } if (!cache.isEmpty()) return cache.remove(0); return null; } - private void findFluidAtOrAbove(BlockPos pos, Fluid fluid) { + + private void findFluidAtOrAbove(BlockPos pos, Fluid targetFluid) { Queue queue = new LinkedList<>(); Set visited = new HashSet<>(); queue.add(pos); while (!queue.isEmpty()) { - BlockPos nextElement = queue.poll(); - if (visited.contains(nextElement) || nextElement.getDistance(pos.getX(), nextElement.getY(), pos.getZ()) > RANGE) + BlockPos next = queue.poll(); + + if (visited.contains(next) || next.getDistance(pos.getX(), pos.getY(), pos.getZ()) > RANGE) continue; - Block worldBlock = world.getBlockState(nextElement).getBlock(); - if (worldBlock instanceof IFluidBlock) { - if (fluid == null || ((IFluidBlock) worldBlock).getFluid() == fluid) { - //only add drainable fluids, allow chaining along flowing fluid tho - if (((IFluidBlock) worldBlock).canDrain(world, nextElement)) - cache.add(0, nextElement); - visited.add(nextElement); - queue.add(nextElement.west()); - queue.add(nextElement.east()); - queue.add(nextElement.north()); - queue.add(nextElement.south()); - queue.add(nextElement.up()); + // Robust: never force-load chunks during a flood fill + if (!world.isBlockLoaded(next)) + continue; + + IBlockState state = world.getBlockState(next); + Block block = state.getBlock(); + + // Case 1: IFluidBlock (existing behavior) + if (block instanceof IFluidBlock) { + IFluidBlock fb = (IFluidBlock) block; + Fluid f = fb.getFluid(); + if (targetFluid == null || f == targetFluid) { + if (fb.canDrain(world, next)) { + cache.add(0, next); // drainable + } + visited.add(next); + queue.add(next.west()); + queue.add(next.east()); + queue.add(next.north()); + queue.add(next.south()); + queue.add(next.up()); + } + continue; + } + + // Case 2: Vanilla BlockLiquid + if (isVanillaLiquid(next)) { + Fluid f = getVanillaFluid(next); + if (f != null && (targetFluid == null || f == targetFluid)) { + // Only sources are drainable; but we still traverse through flowing + if (isVanillaSource(next)) { + cache.add(0, next); + } + visited.add(next); + queue.add(next.west()); + queue.add(next.east()); + queue.add(next.north()); + queue.add(next.south()); + queue.add(next.up()); } } } } + private boolean isRedstoneDisabled() { + return world.isBlockPowered(pos); + } + @Override public boolean canPerformFunction() { - return tank.getFluidAmount() <= tank.getCapacity() && world.getWorldTime() % getFrequencyFromPower() == 0; + if (isRedstoneDisabled()) return false; + if (!shouldRunThisTick(PUMP_INTERVAL_TICKS)) return false; + + // must have at least 100 RF for one bucket operation + if (energy.getUniversalEnergyStored() < getPowerPerOperation()) return false; + + // must be able to accept a full bucket + if ((tank.getCapacity() - tank.getFluidAmount()) < 1000) return false; + + // must have a drainable source available; if not, try to populate cache now (cheap probe) + if (cache.isEmpty()) { + // very small, one-shot version of your getNextBlockLocation() to populate cache + MutableBlockPos currentPos = new MutableBlockPos(pos); + currentPos.move(EnumFacing.DOWN); + + while (currentPos.getY() > 0 && world.isAirBlock(currentPos)) { + currentPos.move(EnumFacing.DOWN); + } + if (currentPos.getY() <= 0) return false; // nothing below + if (!world.isBlockLoaded(currentPos)) return false; + + if (canFitFluid(currentPos)) { + Fluid target = null; + Block worldBlock = world.getBlockState(currentPos).getBlock(); + if (worldBlock instanceof IFluidBlock) { + target = ((IFluidBlock) worldBlock).getFluid(); + } else if (isVanillaLiquid(currentPos)) { + target = getVanillaFluid(currentPos); + } + findFluidAtOrAbove(currentPos, target); + } + } + return !cache.isEmpty(); // only authorize (and thus spend 100 RF) if we have a source to drain } + @Override public IFluidTankProperties[] getTankProperties() { return tank.getTankProperties(); @@ -207,6 +352,34 @@ public String getModularInventoryName() { return "tile.pump.name"; } + @Override + public void onLoad() { + super.onLoad(); + lastFluidType = tank.getFluid() == null ? null : tank.getFluid().getFluid(); + } + + @Override + public void onChunkUnload() { + super.onChunkUnload(); + if (!cache.isEmpty()) cache.clear(); + } + + @Override + public void invalidate() { + super.invalidate(); + if (!cache.isEmpty()) cache.clear(); + } + + @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { + super.writeToNBT(nbt); + nbt.setTag("tank", tank.writeToNBT(new NBTTagCompound())); + return nbt; + } + @Override public void readFromNBT(NBTTagCompound nbt) { + super.readFromNBT(nbt); + tank.readFromNBT(nbt.getCompoundTag("tank")); + } + @Override public boolean canInteractWithContainer(EntityPlayer entity) { return false; diff --git a/src/main/java/zmaster587/advancedRocketry/tile/TileRocketAssemblingMachine.java b/src/main/java/zmaster587/advancedRocketry/tile/TileRocketAssemblingMachine.java index aa77c094d..719945150 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/TileRocketAssemblingMachine.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/TileRocketAssemblingMachine.java @@ -14,6 +14,7 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.ITickable; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; @@ -28,6 +29,7 @@ import zmaster587.advancedRocketry.entity.EntityRocket; import zmaster587.advancedRocketry.item.ItemPackedStructure; import zmaster587.advancedRocketry.network.PacketInvalidLocationNotify; +import zmaster587.advancedRocketry.tile.TileRocketAssemblingMachine.ErrorCodes; import zmaster587.advancedRocketry.tile.hatch.TileSatelliteHatch; import zmaster587.advancedRocketry.util.StorageChunk; import zmaster587.advancedRocketry.util.WeightEngine; @@ -58,7 +60,7 @@ * changed to complete the rocket structure * Also will be used to "build" the rocket components from the placed frames, control fuel flow etc **/ -public class TileRocketAssemblingMachine extends TileEntityRFConsumer implements IButtonInventory, INetworkMachine, IDataSync, IModularInventory, IProgressBar, ILinkableTile { +public class TileRocketAssemblingMachine extends TileEntityRFConsumer implements ITickable, IButtonInventory, INetworkMachine, IDataSync, IModularInventory, IProgressBar, ILinkableTile { protected static final ResourceLocation backdrop = new ResourceLocation("advancedrocketry", "textures/gui/rocketBuilder.png"); protected static final ProgressBarImage verticalProgressBar = new ProgressBarImage(76, 93, 8, 52, 176, 15, 2, 38, 3, 2, EnumFacing.UP, backdrop); @@ -81,6 +83,8 @@ public class TileRocketAssemblingMachine extends TileEntityRFConsumer implements private boolean building; //True is rocket is being built, false if only scanning or otherwise private int lastRocketID; private List blockPos; + private int relinkRetries = 0; // how many relinking tries left + private long nextRelinkAttempt = 0L; // world time for next try public TileRocketAssemblingMachine() { super(100000); @@ -91,25 +95,82 @@ public TileRocketAssemblingMachine() { stats = new StatsRocket(); building = false; prevProgress = 0; - MinecraftForge.EVENT_BUS.register(this); } + private boolean registeredBus = false; + + @Override + public void onLoad() { + if (!world.isRemote && !registeredBus) { + MinecraftForge.EVENT_BUS.register(this); + registeredBus = true; + } + if (!world.isRemote) { + relinkRetries = 15; // give it time + nextRelinkAttempt = world.getTotalWorldTime() + 20; + tryRelinkNow(); // best-effort first shot + } + if (world.isRemote) return; + + // Recompute pad bounds and relink infra to any rockets already on the pad + bbCache = getRocketPadBounds(world, pos); + if (bbCache != null) { + final AxisAlignedBB box = bbCache.grow(1.0E-4, 1.0E-4, 1.0E-4); + List rockets = world.getEntitiesWithinAABB(EntityRocketBase.class, box); + if (!rockets.isEmpty()) { + for (IInfrastructure infra : getConnectedInfrastructure()) { + for (EntityRocketBase r : rockets) { + if (infra instanceof zmaster587.advancedRocketry.tile.infrastructure.TileRocketMonitoringStation) { + ((zmaster587.advancedRocketry.tile.infrastructure.TileRocketMonitoringStation) infra) + .markRocketFromAssembler(r); + } + r.linkInfrastructure(infra); + } + } + } + } + } + @Override public void invalidate() { super.invalidate(); - MinecraftForge.EVENT_BUS.unregister(this); - for (HashedBlockPosition pos : blockPos) { - TileEntity tile = world.getTileEntity(pos.getBlockPos()); - - if (tile instanceof IMultiblock) - ((IMultiblock) tile).setIncomplete(); + unregisterFromBus(); + relinkRetries = 0; + nextRelinkAttempt = 0L; + // Notify linked multiblocks BEFORE clearing (server only) + if (world != null && !world.isRemote) { + for (HashedBlockPosition p : blockPos) { + TileEntity te = world.getTileEntity(p.getBlockPos()); + if (te instanceof IMultiblock) { + ((IMultiblock) te).setIncomplete(); + } + } } + + // Clear caches + bbCache = null; + stats.reset(); + blockPos.clear(); } @Override public void onChunkUnload() { super.onChunkUnload(); - MinecraftForge.EVENT_BUS.unregister(this); + unregisterFromBus(); + relinkRetries = 0; + nextRelinkAttempt = 0L; + // Clear caches + bbCache = null; + stats.reset(); + blockPos.clear(); + } + + + private void unregisterFromBus() { + if (registeredBus) { + MinecraftForge.EVENT_BUS.unregister(this); + registeredBus = false; + } } public ErrorCodes getStatus() { @@ -202,6 +263,8 @@ public int getPowerPerOperation() { @Override public void performFunction() { + + if (!isScanning()) return; if (progress >= (totalProgress * MAXSCANDELAY)) { if (!world.isRemote) { if (building) @@ -262,10 +325,10 @@ public AxisAlignedBB scanRocket(World world, BlockPos pos2, AxisAlignedBB bb) { double buffer = 0.0001; AxisAlignedBB bufferedBB = bbCache.grow(buffer, buffer, buffer); List rockets = world.getEntitiesWithinAABB(EntityRocket.class, bufferedBB); - if (rockets.size() == 1){ // only if axactly one rocket is here + if (rockets.size() == 1){ rockets.get(0).recalculateStats(); this.stats = rockets.get(0).stats; - status = ErrorCodes.ALREADY_ASSEMBLED; // to prevent assembly + status = ErrorCodes.ALREADY_ASSEMBLED; return null; } } @@ -423,17 +486,23 @@ public AxisAlignedBB scanRocket(World world, BlockPos pos2, AxisAlignedBB bb) { thrustNuclearTotalLimit = (nuclearWorkingFluidUse * thrustNuclearNozzleLimit) / nuclearWorkingFluidUseMax; } - //Set fuel stats - //Thrust depending on rocket type + // Set fuel stats + // Thrust depending on rocket type stats.setBaseFuelRate(FuelType.LIQUID_MONOPROPELLANT, monopropellantfuelUse); - stats.setBaseFuelRate(FuelType.LIQUID_BIPROPELLANT, bipropellantfuelUse); - stats.setBaseFuelRate(FuelType.LIQUID_OXIDIZER, bipropellantfuelUse); + stats.setBaseFuelRate(FuelType.LIQUID_BIPROPELLANT, bipropellantfuelUse); + stats.setBaseFuelRate(FuelType.LIQUID_OXIDIZER, bipropellantfuelUse); stats.setBaseFuelRate(FuelType.NUCLEAR_WORKING_FLUID, nuclearWorkingFluidUse); - //Fuel storage depending on rocket type - stats.setFuelCapacity(FuelType.LIQUID_MONOPROPELLANT, fuelCapacityMonopropellant); - stats.setFuelCapacity(FuelType.LIQUID_BIPROPELLANT, fuelCapacityBipropellant); - stats.setFuelCapacity(FuelType.LIQUID_OXIDIZER, fuelCapacityOxidizer); - stats.setFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID, fuelCapacityNuclearWorkingFluid); + + stats.setFuelRate(FuelType.LIQUID_MONOPROPELLANT, monopropellantfuelUse); + stats.setFuelRate(FuelType.LIQUID_BIPROPELLANT, bipropellantfuelUse); + stats.setFuelRate(FuelType.LIQUID_OXIDIZER, bipropellantfuelUse); + stats.setFuelRate(FuelType.NUCLEAR_WORKING_FLUID, nuclearWorkingFluidUse); + + // Fuel storage depending on rocket type + stats.setFuelCapacity(FuelType.LIQUID_MONOPROPELLANT, fuelCapacityMonopropellant); + stats.setFuelCapacity(FuelType.LIQUID_BIPROPELLANT, fuelCapacityBipropellant); + stats.setFuelCapacity(FuelType.LIQUID_OXIDIZER, fuelCapacityOxidizer); + stats.setFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID, fuelCapacityNuclearWorkingFluid); //Non-fuel stats stats.setWeight(weight); @@ -445,27 +514,63 @@ public AxisAlignedBB scanRocket(World world, BlockPos pos2, AxisAlignedBB bb) { int totalFuelUse = bipropellantfuelUse + nuclearWorkingFluidUse + monopropellantfuelUse; //System.out.println("rocket fuel use:"+totalFuelUse); + // Biprop requirement: if any bipropellant thrust exists, require both tanks + if (thrustBipropellant > 0) { + if (fuelCapacityBipropellant <= 0 || fuelCapacityOxidizer <= 0) { + status = ErrorCodes.NOFUEL; + return new AxisAlignedBB(actualMinX, actualMinY, actualMinZ, actualMaxX, actualMaxY, actualMaxZ); + } + } + //Set status - if (invalidBlock) + if (invalidBlock) { status = ErrorCodes.INVALIDBLOCK; - else if (((fuelCapacityBipropellant > 0 && totalFuel > fuelCapacityBipropellant) || (fuelCapacityMonopropellant > 0 && totalFuel > fuelCapacityMonopropellant) || (fuelCapacityNuclearWorkingFluid > 0 && totalFuel > fuelCapacityNuclearWorkingFluid)) + + } else if (((fuelCapacityBipropellant > 0 && totalFuel > fuelCapacityBipropellant) + || (fuelCapacityMonopropellant > 0 && totalFuel > fuelCapacityMonopropellant) + || (fuelCapacityNuclearWorkingFluid > 0 && totalFuel > fuelCapacityNuclearWorkingFluid)) || - ((thrustBipropellant > 0 && totalFuelUse > bipropellantfuelUse) || (thrustMonopropellant > 0 && totalFuelUse > monopropellantfuelUse) || (thrustNuclearTotalLimit > 0 && totalFuelUse > nuclearWorkingFluidUse))) + ((thrustBipropellant > 0 && totalFuelUse > bipropellantfuelUse) + || (thrustMonopropellant > 0 && totalFuelUse > monopropellantfuelUse) + || (thrustNuclearTotalLimit > 0 && totalFuelUse > nuclearWorkingFluidUse))) { status = ErrorCodes.COMBINEDTHRUST; - else if (!hasGuidance && !hasSatellite) + + } else if (!hasGuidance && !hasSatellite) { status = ErrorCodes.NOGUIDANCE; - else if (getThrust() <= getNeededThrust()) + + } else if (getThrust() <= getNeededThrust()) { status = ErrorCodes.NOENGINES; - else if (((thrustBipropellant > 0) && !hasEnoughFuel(FuelType.LIQUID_BIPROPELLANT)) || ((thrustMonopropellant > 0) && !hasEnoughFuel(FuelType.LIQUID_MONOPROPELLANT)) || ((thrustNuclearTotalLimit > 0) && !hasEnoughFuel(FuelType.NUCLEAR_WORKING_FLUID))) + + } else if (thrustBipropellant > 0 && (fuelCapacityBipropellant <= 0 || fuelCapacityOxidizer <= 0)) { + // Biprop engines require BOTH bipropellant AND oxidizer capacity + status = ErrorCodes.NOFUEL; + + } else if (((thrustBipropellant > 0) && !hasEnoughFuel(FuelType.LIQUID_BIPROPELLANT)) + || ((thrustMonopropellant > 0) && !hasEnoughFuel(FuelType.LIQUID_MONOPROPELLANT)) + || ((thrustNuclearTotalLimit > 0) && !hasEnoughFuel(FuelType.NUCLEAR_WORKING_FLUID))) { status = ErrorCodes.NOFUEL; - else + + } else { status = ErrorCodes.SUCCESS; + } } - - return new AxisAlignedBB(actualMinX, actualMinY, actualMinZ, actualMaxX, actualMaxY, actualMaxZ); + + // Normalize integer mins/maxes first + int minXi = Math.min(actualMinX, actualMaxX); + int minYi = Math.min(actualMinY, actualMaxY); + int minZi = Math.min(actualMinZ, actualMaxZ); + int maxXi = Math.max(actualMinX, actualMaxX); + int maxYi = Math.max(actualMaxY, actualMinY); + int maxZi = Math.max(actualMinZ, actualMaxZ); + + // use BlockPos ctor so the AABB is [min, max+1) in block space + return new AxisAlignedBB( + new BlockPos(minXi, minYi, minZi), + new BlockPos(maxXi, maxYi, maxZi) + ); } - private void removeReplaceableBlocks(AxisAlignedBB bb) { + protected void removeReplaceableBlocks(AxisAlignedBB bb) { for (int yCurr = (int) bb.minY; yCurr <= bb.maxY; yCurr++) { for (int xCurr = (int) bb.minX; xCurr <= bb.maxX; xCurr++) { for (int zCurr = (int) bb.minZ; zCurr <= bb.maxZ; zCurr++) { @@ -485,47 +590,79 @@ private void removeReplaceableBlocks(AxisAlignedBB bb) { } } + private static boolean isEmptyAABB(@Nullable AxisAlignedBB b) { + return b == null || b.maxX < b.minX || b.maxY < b.minY || b.maxZ < b.minZ; + } + + + private static AxisAlignedBB normalize(AxisAlignedBB b) { + double minX = Math.min(b.minX, b.maxX); + double minY = Math.min(b.minY, b.maxY); + double minZ = Math.min(b.minZ, b.maxZ); + double maxX = Math.max(b.minX, b.maxX); + double maxY = Math.max(b.minY, b.maxY); + double maxZ = Math.max(b.minZ, b.maxZ); + return new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); + } + + public void assembleRocket() { + // server only + need a pad cache + if (world.isRemote || bbCache == null) return; - if (bbCache == null || world.isRemote) - return; - // Need to scan again b/c something may have changed - AxisAlignedBB rocketBB = scanRocket(world, pos, bbCache); + // Re-scan to get a tight non-air AABB and fresh stats/status + final AxisAlignedBB scanBB = scanRocket(world, pos, bbCache); + if (status != ErrorCodes.SUCCESS || scanBB == null) return; - if (status != ErrorCodes.SUCCESS) + // Normalize and defensively guard against degenerate boxes + final AxisAlignedBB rocketBB = normalize(scanBB); + if (isEmptyAABB(rocketBB)) { + status = ErrorCodes.FAIL_CUT; return; + } - // Remove replacable blocks that don't belong on the rocket - removeReplaceableBlocks(bbCache); + // Remove replaceable/blacklisted blocks *inside the tightened bounds* + removeReplaceableBlocks(rocketBB); - StorageChunk storageChunk; + // Cut the world using the tightened box (avoid pad air) + final StorageChunk storageChunk; try { - storageChunk = StorageChunk.cutWorldBB(world, bbCache); - } catch (NegativeArraySizeException e) { + storageChunk = StorageChunk.cutWorldBB(world, rocketBB); + } catch (Throwable t) { // cover NegativeArraySizeException & other edge errors + status = ErrorCodes.FAIL_CUT; return; } - EntityRocket rocket = new EntityRocket(world, storageChunk, stats.copy(), - rocketBB.minX + (rocketBB.maxX - rocketBB.minX) / 2f + .5f, - this.getPos().getY(), - rocketBB.minZ + (rocketBB.maxZ - rocketBB.minZ) / 2f + .5f); + // Center spawn on tightened AABB + final double cx = rocketBB.minX + (rocketBB.maxX - rocketBB.minX) / 2.0 + 0.5; + final double cz = rocketBB.minZ + (rocketBB.maxZ - rocketBB.minZ) / 2.0 + 0.5; + final double cy = this.getPos().getY(); + EntityRocket rocket = new EntityRocket(world, storageChunk, stats.copy(), cx, cy, cz); world.spawnEntity(rocket); - NBTTagCompound nbtdata = new NBTTagCompound(); + NBTTagCompound nbtdata = new NBTTagCompound(); rocket.writeToNBT(nbtdata); - PacketHandler.sendToNearby(new PacketEntity(rocket, (byte) 0, nbtdata), rocket.world.provider.getDimension(), this.pos, 64); + PacketHandler.sendToNearby(new PacketEntity(rocket, (byte) 0, nbtdata), + rocket.world.provider.getDimension(), this.pos, 64); + // Finish & link as before stats.reset(); this.status = ErrorCodes.FINISHED; this.markDirty(); world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); for (IInfrastructure infrastructure : getConnectedInfrastructure()) { + if (infrastructure instanceof zmaster587.advancedRocketry.tile.infrastructure.TileRocketMonitoringStation) { + ((zmaster587.advancedRocketry.tile.infrastructure.TileRocketMonitoringStation) infrastructure) + .markRocketFromAssembler(rocket); + } rocket.linkInfrastructure(infrastructure); } - scanRocket(world, pos, bbCache); // to show stats + + // Rescan so UI immediately reflects the post-build state + scanRocket(world, pos, bbCache); } /** @@ -796,6 +933,9 @@ public void useNetworkData(EntityPlayer player, Side side, byte id, } protected void updateText() { + if (thrustText == null || weightText == null || fuelText == null || accelerationText == null || errorText == null) { + return; + } thrustText.setText(isScanning() ? (LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.thrust") + ": ???") : String.format("%s: %dkN", LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.thrust"), getThrust())); weightText.setText(isScanning() ? (LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.weight") + ": ???") : String.format("%s: %.2fkN", LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.weight"), (getWeight() * getGravityMultiplier()))); fuelText.setText(isScanning() ? (LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.fuel") + ": ???") : String.format("%s: %dmb/s", LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.fuel"), 20* getRocketStats().getFuelRate((stats.getFuelCapacity(FuelType.LIQUID_MONOPROPELLANT) > 0) ? FuelType.LIQUID_MONOPROPELLANT : (stats.getFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID) > 0) ? FuelType.NUCLEAR_WORKING_FLUID : FuelType.LIQUID_BIPROPELLANT))); @@ -812,6 +952,17 @@ else if (ErrorCodes.INCOMPLETESTRCUTURE.equals(getStatus())) @Override public List getModules(int ID, EntityPlayer player) { + + // Automatically set status to unscanned if no rocket is present when opening GUI + if (!world.isRemote && status == ErrorCodes.ALREADY_ASSEMBLED) { + AxisAlignedBB box = (bbCache != null) ? bbCache : getRocketPadBounds(world, pos); + if (box == null || world.getEntitiesWithinAABB(EntityRocket.class, box).isEmpty()) { + status = ErrorCodes.UNSCANNED; + markDirty(); + } + } + + List modules = new LinkedList<>(); modules.add(new ModulePower(160, 90, this)); @@ -967,7 +1118,7 @@ public int getData(int id) { switch (id) { case 0: - return (int)(getRocketStats().getWeight_NoFuel()*1000);// because it is a float really so take it *1000 + return (int)(getRocketStats().getWeight_NoFuel()*1000); case 1: return getRocketStats().getThrust(); case 2: @@ -1009,7 +1160,6 @@ public int getData(int id) { @Override public void onInventoryButtonPressed(int buttonId) { PacketHandler.sendToServer(new PacketMachine(this, (byte) (buttonId))); - //updateText(); } @Override @@ -1089,56 +1239,62 @@ public void removeConnectedInfrastructure(TileEntity tile) { } public List getConnectedInfrastructure() { - List infrastructure = new LinkedList<>(); - - Iterator iter = blockPos.iterator(); - - while (iter.hasNext()) { - HashedBlockPosition position = iter.next(); - TileEntity tile = world.getTileEntity(position.getBlockPos()); - if (tile instanceof IInfrastructure) { - infrastructure.add((IInfrastructure) tile); - } else - iter.remove(); + List list = new LinkedList<>(); + for (HashedBlockPosition position : blockPos) { + TileEntity te = world.getTileEntity(position.getBlockPos()); + if (te instanceof IInfrastructure) { + list.add((IInfrastructure) te); + } } - - return infrastructure; + return list; } @SubscribeEvent - public void onRocketLand(RocketLandedEvent event) { - if (event.world.isRemote) - return; - EntityRocketBase rocket = (EntityRocketBase) event.getEntity(); - - - //This apparently happens sometimes - if (world == null) { - AdvancedRocketry.logger.debug("World null for rocket builder during rocket land event @ " + this.pos); - return; + public void onRocketLand(RocketLandedEvent e) { + // Server/world guard + if (e.world.isRemote || e.world != this.world) return; + + // Ensure we have pad bounds + bbCache = getRocketPadBounds(world, pos); + if (bbCache == null) return; + + // Make sure the event entity is a rocket + final net.minecraft.entity.Entity ent = e.getEntity(); + if (!(ent instanceof EntityRocketBase)) return; + final EntityRocketBase landed = (EntityRocketBase) ent; + + // Quick membership test with tiny epsilon + final AxisAlignedBB box = bbCache.grow(1.0E-4, 1.0E-4, 1.0E-4); + if (!landed.getEntityBoundingBox().intersects(box)) return; + + // Track rocket id and (re)link infra + lastRocketID = landed.getEntityId(); + for (IInfrastructure infra : getConnectedInfrastructure()) { + if (infra instanceof zmaster587.advancedRocketry.tile.infrastructure.TileRocketMonitoringStation) { + ((zmaster587.advancedRocketry.tile.infrastructure.TileRocketMonitoringStation) infra) + .markRocketFromAssembler(landed); + } + landed.linkInfrastructure(infra); } - if (getBBCache() == null) { - bbCache = getRocketPadBounds(world, pos); - } - if (getBBCache() != null) { - double buffer = 0.0001; - AxisAlignedBB bufferedBB = bbCache.grow(buffer, buffer, buffer); - List rockets = world.getEntitiesWithinAABB(EntityRocketBase.class, bufferedBB); - - if (rockets.contains(rocket)) { - lastRocketID = rocket.getEntityId(); - for (IInfrastructure infrastructure : getConnectedInfrastructure()) { - rocket.linkInfrastructure(infrastructure); - } - scanRocket(world,pos, bbCache); // rescan on landing - - PacketHandler.sendToPlayersTrackingEntity(new PacketMachine(this, (byte) 3), rocket); - } + // only fast-path when exactly one rocket in the pad + List rockets = world.getEntitiesWithinAABB(EntityRocket.class, box); + if (rockets.size() == 1) { + EntityRocket r = rockets.get(0); + r.recalculateStats(); + this.stats = r.stats.copy(); + this.status = ErrorCodes.ALREADY_ASSEMBLED; + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + } else { + // Fallback: rescan if something odd happens + scanRocket(world, pos, bbCache); } + PacketHandler.sendToPlayersTrackingEntity(new PacketMachine(this, (byte)3), landed); } + protected enum ErrorCodes { SUCCESS(LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.success")), NOFUEL(LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.nofuel")), @@ -1155,7 +1311,11 @@ protected enum ErrorCodes { OUTPUTBLOCKED(LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.outputblocked")), INVALIDBLOCK(LibVulpes.proxy.getLocalizedString("msg.rocketbuild.invalidblock")), COMBINEDTHRUST(LibVulpes.proxy.getLocalizedString("msg.rocketbuild.combinedthrust")), - ALREADY_ASSEMBLED("rocket already assembled"); + ALREADY_ASSEMBLED(LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.alreadyassembled")), + UNSCANNED_STATION(LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.unscanned_station")), + FAIL_CUT(LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.fail_cut")), + NOINTAKE(LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.nointake")), + NOTANK(LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.notank")); String code; @@ -1167,4 +1327,42 @@ public String getErrorCode() { return code; } } + + @Override + public void update() { + super.update(); + if (world.isRemote) return; + + if (relinkRetries > 0 && world.getTotalWorldTime() >= nextRelinkAttempt) { + if (tryRelinkNow()) { + relinkRetries = 0; + } else { + relinkRetries--; + nextRelinkAttempt = world.getTotalWorldTime() + 20; // 1s + } + } + } + + private boolean tryRelinkNow() { + if (bbCache == null) bbCache = getRocketPadBounds(world, pos); + if (bbCache == null) return false; + + AxisAlignedBB box = bbCache.grow(1.0e-4,1.0e-4,1.0e-4); + java.util.List rockets = world.getEntitiesWithinAABB(EntityRocketBase.class, box); + if (rockets.isEmpty()) return false; + + java.util.List infraNow = getConnectedInfrastructure(); + if (infraNow.isEmpty()) return false; + + for (EntityRocketBase r : rockets) { + for (IInfrastructure i : infraNow) { + if (i instanceof zmaster587.advancedRocketry.tile.infrastructure.TileRocketMonitoringStation) { + ((zmaster587.advancedRocketry.tile.infrastructure.TileRocketMonitoringStation) i) + .markRocketFromAssembler(r); + } + r.linkInfrastructure(i); + } + } + return true; + } } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/TileStationAssembler.java b/src/main/java/zmaster587/advancedRocketry/tile/TileStationAssembler.java index 74b2fdaaf..abc674c3c 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/TileStationAssembler.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/TileStationAssembler.java @@ -63,11 +63,13 @@ public boolean canScan() { public AxisAlignedBB scanRocket(World world, BlockPos pos2, AxisAlignedBB bb) { int actualMinX = (int) bb.maxX, - actualMinY = (int) bb.maxY, - actualMinZ = (int) bb.maxZ, - actualMaxX = (int) bb.minX, - actualMaxY = (int) bb.minY, - actualMaxZ = (int) bb.minZ; + actualMinY = (int) bb.maxY, + actualMinZ = (int) bb.maxZ, + actualMaxX = (int) bb.minX, + actualMaxY = (int) bb.minY, + actualMaxZ = (int) bb.minZ; + + boolean foundNonAir = false; for (int xCurr = (int) bb.minX; xCurr <= bb.maxX; xCurr++) { for (int zCurr = (int) bb.minZ; zCurr <= bb.maxZ; zCurr++) { @@ -76,29 +78,32 @@ public AxisAlignedBB scanRocket(World world, BlockPos pos2, AxisAlignedBB bb) { BlockPos posCurr = new BlockPos(xCurr, yCurr, zCurr); if (!world.isAirBlock(posCurr)) { - if (xCurr < actualMinX) - actualMinX = xCurr; - if (yCurr < actualMinY) - actualMinY = yCurr; - if (zCurr < actualMinZ) - actualMinZ = zCurr; - if (xCurr > actualMaxX) - actualMaxX = xCurr; - if (yCurr > actualMaxY) - actualMaxY = yCurr; - if (zCurr > actualMaxZ) - actualMaxZ = zCurr; + foundNonAir = true; + + if (xCurr < actualMinX) actualMinX = xCurr; + if (yCurr < actualMinY) actualMinY = yCurr; + if (zCurr < actualMinZ) actualMinZ = zCurr; + if (xCurr > actualMaxX) actualMaxX = xCurr; + if (yCurr > actualMaxY) actualMaxY = yCurr; + if (zCurr > actualMaxZ) actualMaxZ = zCurr; } } } } - status = ErrorCodes.SUCCESS_STATION; + // Tell the player whats up + if (!foundNonAir) { + status = ErrorCodes.EMPTY; // nothing to pack inside bb + return bb; // sanity check + } else { + status = ErrorCodes.SUCCESS_STATION; // ok to proceed with packing + } return new AxisAlignedBB(actualMinX, actualMinY, actualMinZ, actualMaxX, actualMaxY, actualMaxZ); } + @Override public void assembleRocket() { if (!world.isRemote) { @@ -113,6 +118,9 @@ public void assembleRocket() { try { storageChunk = StorageChunk.cutWorldBB(world, bbCache); } catch (NegativeArraySizeException e) { + status = ErrorCodes.FAIL_CUT; + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); return; } @@ -152,20 +160,38 @@ public void assembleRocket() { @Override protected void updateText() { - if (!world.isRemote) { - if (getRocketPadBounds(world, pos) == null) + if (world != null && !world.isRemote) { + if (getRocketPadBounds(world, pos) == null) { setStatus(ErrorCodes.INCOMPLETESTRCUTURE.ordinal()); - else if (ErrorCodes.INCOMPLETESTRCUTURE.equals(getStatus())) - setStatus(ErrorCodes.UNSCANNED.ordinal()); + } else if (ErrorCodes.INCOMPLETESTRCUTURE.equals(getStatus())) { + setStatus(ErrorCodes.UNSCANNED_STATION.ordinal()); + } } - errorText.setText(status.getErrorCode()); + if (errorText != null) { + errorText.setText(status.getErrorCode()); + } } @Override public List getModules(int ID, EntityPlayer player) { List modules = new LinkedList<>(); + // GUI-open reset errorcode if pad is valid and we're idle + if (!world.isRemote) { + AxisAlignedBB bounds = getRocketPadBounds(world, pos); + if (bounds == null) { + setStatus(ErrorCodes.INCOMPLETESTRCUTURE.ordinal()); + } else if (!isScanning()) { + ErrorCodes s = getStatus(); + if (s == ErrorCodes.SUCCESS_STATION || s == ErrorCodes.SUCCESS || + s == ErrorCodes.FINISHED || s == ErrorCodes.EMPTY || + s == ErrorCodes.UNSCANNED) { + setStatus(ErrorCodes.UNSCANNED_STATION.ordinal()); + } + } + } + modules.add(new ModulePower(160, 30, this)); modules.add(new ModuleProgress(149, 30, 2, verticalProgressBar, this)); @@ -177,6 +203,7 @@ public List getModules(int ID, EntityPlayer player) { buttonBuild.setColor(0xFFFF2222); modules.add(errorText = new ModuleText(5, 22, "", 0xFFFFFF22)); modules.add(new ModuleSync(4, this)); + modules.add(new ModuleSync(2, this)); // sync error codes to client (on change) updateText(); @@ -190,19 +217,20 @@ public List getModules(int ID, EntityPlayer player) { @Override - public void useNetworkData(EntityPlayer player, Side side, byte id, - NBTTagCompound nbt) { + public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompound nbt) { + + super.useNetworkData(player, side, id, nbt); + // recompute AFTER super boolean isScanningFlag = !isScanning() && canScan(); - super.useNetworkData(player, side, id, nbt); if (id == 1 && isScanningFlag) { - storedId = (long) ItemStationChip.getUUID(inventory.getStackInSlot(1)); if (storedId == 0) storedId = null; } } + @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); diff --git a/src/main/java/zmaster587/advancedRocketry/tile/TileUnmannedVehicleAssembler.java b/src/main/java/zmaster587/advancedRocketry/tile/TileUnmannedVehicleAssembler.java index 6869bb1ee..7e5b26981 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/TileUnmannedVehicleAssembler.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/TileUnmannedVehicleAssembler.java @@ -18,6 +18,7 @@ import zmaster587.advancedRocketry.entity.EntityStationDeployedRocket; import zmaster587.advancedRocketry.network.PacketInvalidLocationNotify; import zmaster587.advancedRocketry.util.StorageChunk; +import zmaster587.advancedRocketry.util.WeightEngine; import zmaster587.libVulpes.block.BlockFullyRotatable; import zmaster587.libVulpes.block.RotatableBlock; import zmaster587.libVulpes.network.PacketEntity; @@ -99,64 +100,106 @@ public AxisAlignedBB getRocketPadBounds(World world, BlockPos pos2) { return new AxisAlignedBB(xMin, yCurrent, zMin, xMax, yCurrent + yMax - 1, zMax); } + @Override public void assembleRocket() { - if (bbCache == null || world.isRemote) - return; - //Need to scan again b/c something may have changed - scanRocket(world, getPos(), bbCache); + if (bbCache == null || world.isRemote) return; - if (status != ErrorCodes.SUCCESS) - return; - StorageChunk storageChunk; + // 1) Rescan like the parent (may update stats/status and tighten AABB) + AxisAlignedBB rocketBB = scanRocket(world, getPos(), bbCache); + if (status != ErrorCodes.SUCCESS || rocketBB == null) return; - //Breaks if nothing is there + // 2) Remove replaceables **inside the tight box** + removeReplaceableBlocks(rocketBB); + + // 3) Cut the world using the **tight** AABB + final StorageChunk storageChunk; try { - storageChunk = StorageChunk.cutWorldBB(world, bbCache); - } catch (NegativeArraySizeException e) { + storageChunk = StorageChunk.cutWorldBB(world, rocketBB); + } catch (Throwable t) { // covers NegativeArraySizeException, etc. return; } + // 4) Spawn the SD rocket, centered from the *rescanned* bbox + final double cx = rocketBB.minX + (rocketBB.maxX - rocketBB.minX) / 2f + 0.5f; + final double cz = rocketBB.minZ + (rocketBB.maxZ - rocketBB.minZ) / 2f + 0.5f; + final double cy = this.getPos().getY(); - EntityStationDeployedRocket rocket = new EntityStationDeployedRocket(world, storageChunk, stats.copy(), bbCache.minX + (bbCache.maxX - bbCache.minX) / 2f + .5f, getPos().getY(), bbCache.minZ + (bbCache.maxZ - bbCache.minZ) / 2f + .5f); + EntityStationDeployedRocket rocket = + new EntityStationDeployedRocket(world, storageChunk, stats.copy(), cx, cy, cz); - //TODO: setRocketDirection + // Orientations for SD rockets rocket.forwardDirection = RotatableBlock.getFront(world.getBlockState(getPos())).getOpposite(); rocket.launchDirection = EnumFacing.DOWN; - //Change engine direction + // 5) Rotate *all* engine types to match forwardDirection (defensive: only if block supports FACING) for (int x = 0; x < storageChunk.getSizeX(); x++) { for (int y = 0; y < storageChunk.getSizeY(); y++) { for (int z = 0; z < storageChunk.getSizeZ(); z++) { + BlockPos bp = new BlockPos(x, y, z); + IBlockState st = storageChunk.getBlockState(bp); + Block b = st.getBlock(); + + boolean isEngine = (b instanceof BlockRocketMotor) + || (b instanceof BlockBipropellantRocketMotor) + || (b instanceof BlockNuclearRocketMotor); - BlockPos pos3 = new BlockPos(x, y, z); - if (storageChunk.getBlockState(pos3).getBlock() instanceof BlockRocketMotor) { - storageChunk.setBlockState(pos3, storageChunk.getBlockState(pos3).withProperty(BlockFullyRotatable.FACING, rocket.forwardDirection)); + if (isEngine && st.getPropertyKeys().contains(BlockFullyRotatable.FACING)) { + storageChunk.setBlockState(bp, st.withProperty(BlockFullyRotatable.FACING, rocket.forwardDirection)); } } } } + // 6) Spawn + sync world.spawnEntity(rocket); - NBTTagCompound nbtdata = new NBTTagCompound(); - - rocket.writeToNBT(nbtdata); - PacketHandler.sendToNearby(new PacketEntity(rocket, (byte) 0, nbtdata), rocket.world.provider.getDimension(), this.pos, 64); - - stats.reset(); - this.status = ErrorCodes.UNSCANNED; - this.markDirty(); + NBTTagCompound nbt = new NBTTagCompound(); + rocket.writeToNBT(nbt); + PacketHandler.sendToNearby(new PacketEntity(rocket, (byte) 0, nbt), + rocket.world.provider.getDimension(), this.pos, 64); + // Link existing infrastructure (same order as parent) for (IInfrastructure infrastructure : getConnectedInfrastructure()) { rocket.linkInfrastructure(infrastructure); } + + // 7) Directly stamp tile stats from the entity we just created + rocket.recalculateStats(); + this.stats = rocket.stats.copy(); + + // Now finish up — and DO NOT reset after this + this.status = ErrorCodes.FINISHED; + this.markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + + // Rescan to immediately show fresh stats after build + scanRocket(world, getPos(), bbCache); } - //TODO get direction of rocket + @Override public AxisAlignedBB scanRocket(World world, BlockPos pos2, AxisAlignedBB bb) { - // TODO Refactor! Duplicated with TileRocketAssemblingMachine + // Always refresh local bounds first + AxisAlignedBB fresh = getRocketPadBounds(world, getPos()); + if (fresh == null) { + status = ErrorCodes.INCOMPLETESTRCUTURE; // upstream typo + return null; // avoid using stale bb + } + bbCache = fresh; + bb = fresh; // ensure loops below use the fresh bounds + + // fast-path: rocket entity already present? + final AxisAlignedBB buffered = bb.grow(1.0e-4, 1.0e-4, 1.0e-4); + java.util.List sdr = + world.getEntitiesWithinAABB(EntityStationDeployedRocket.class, buffered); + if (sdr.size() == 1) { + EntityStationDeployedRocket r = sdr.get(0); + r.recalculateStats(); + this.stats = r.stats.copy(); + this.status = ErrorCodes.ALREADY_ASSEMBLED; + return null; + } int thrustMonopropellant = 0; int thrustBipropellant = 0; int thrustNuclearNozzleLimit = 0; @@ -169,45 +212,36 @@ public AxisAlignedBB scanRocket(World world, BlockPos pos2, AxisAlignedBB bb) { int fuelCapacityBipropellant = 0; int fuelCapacityOxidizer = 0; int fuelCapacityNuclearWorkingFluid = 0; - int numBlocks = 0; - float drillPower = 0f; + float weight = 0f; + stats.reset(); int actualMinX = (int) bb.maxX, - actualMinY = (int) bb.maxY, - actualMinZ = (int) bb.maxZ, - actualMaxX = (int) bb.minX, - actualMaxY = (int) bb.minY, - actualMaxZ = (int) bb.minZ; - + actualMinY = (int) bb.maxY, + actualMinZ = (int) bb.maxZ, + actualMaxX = (int) bb.minX, + actualMaxY = (int) bb.minY, + actualMaxZ = (int) bb.minZ; + // tighten AABB to non-air for (int xCurr = (int) bb.minX; xCurr <= bb.maxX; xCurr++) { for (int zCurr = (int) bb.minZ; zCurr <= bb.maxZ; zCurr++) { for (int yCurr = (int) bb.minY; yCurr <= bb.maxY; yCurr++) { - - BlockPos currPos = new BlockPos(xCurr, yCurr, zCurr); - - if (!world.isAirBlock(currPos)) { - if (xCurr < actualMinX) - actualMinX = xCurr; - if (yCurr < actualMinY) - actualMinY = yCurr; - if (zCurr < actualMinZ) - actualMinZ = zCurr; - if (xCurr > actualMaxX) - actualMaxX = xCurr; - if (yCurr > actualMaxY) - actualMaxY = yCurr; - if (zCurr > actualMaxZ) - actualMaxZ = zCurr; + BlockPos p = new BlockPos(xCurr, yCurr, zCurr); + if (!world.isAirBlock(p)) { + if (xCurr < actualMinX) actualMinX = xCurr; + if (yCurr < actualMinY) actualMinY = yCurr; + if (zCurr < actualMinZ) actualMinZ = zCurr; + if (xCurr > actualMaxX) actualMaxX = xCurr; + if (yCurr > actualMaxY) actualMaxY = yCurr; + if (zCurr > actualMaxZ) actualMaxZ = zCurr; } } } } - boolean hasSatellite = false; - boolean hasGuidance = false; boolean invalidBlock = false; + boolean foundFluidTank = false; int fluidCapacity = 0; if (verifyScan(bb, world)) { @@ -216,124 +250,227 @@ public AxisAlignedBB scanRocket(World world, BlockPos pos2, AxisAlignedBB bb) { for (int zCurr = (int) bb.minZ; zCurr <= bb.maxZ; zCurr++) { BlockPos currPos = new BlockPos(xCurr, yCurr, zCurr); - if (!world.isAirBlock(currPos)) { - IBlockState state = world.getBlockState(currPos); - Block block = state.getBlock(); - - if (ARConfiguration.getCurrentConfig().blackListRocketBlocks.contains(block)) { - if (!block.isReplaceable(world, currPos)) { - invalidBlock = true; - if (!world.isRemote) - PacketHandler.sendToNearby(new PacketInvalidLocationNotify(new HashedBlockPosition(xCurr, yCurr, zCurr)), world.provider.getDimension(), getPos(), 64); + if (world.isAirBlock(currPos)) continue; + + IBlockState state = world.getBlockState(currPos); + Block block = state.getBlock(); + + // blacklist guard + if (ARConfiguration.getCurrentConfig().blackListRocketBlocks.contains(block)) { + if (!block.isReplaceable(world, currPos)) { + invalidBlock = true; + if (!world.isRemote) { + PacketHandler.sendToNearby( + new PacketInvalidLocationNotify(new HashedBlockPosition(xCurr, yCurr, zCurr)), + world.provider.getDimension(), getPos(), 64 + ); } - continue; } + continue; + } - numBlocks++; - - //If rocketEngine increaseThrust - if (block instanceof IRocketEngine) { - if (block instanceof BlockNuclearRocketMotor) { - nuclearWorkingFluidUseMax += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); - thrustNuclearNozzleLimit += ((IRocketEngine) block).getThrust(world, currPos); - } else if (block instanceof BlockBipropellantRocketMotor) { - bipropellantfuelUse += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); - thrustBipropellant += ((IRocketEngine) block).getThrust(world, currPos); - } else if (block instanceof BlockRocketMotor) { - monopropellantfuelUse += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); - thrustMonopropellant += ((IRocketEngine) block).getThrust(world, currPos); - } + if (ARConfiguration.getCurrentConfig().advancedWeightSystem) { + weight += WeightEngine.INSTANCE.getWeight(world, currPos); + } else { + weight += 1f; // fallback: count blocks + } - stats.addEngineLocation(xCurr - actualMinX - ((float) (actualMaxX - actualMinX) / 2f), yCurr - actualMinY, zCurr - actualMinZ - ((float) (actualMaxZ - actualMinZ) / 2f)); - //stats.addEngineLocation(xCurr - actualMinX, yCurr - actualMinY, zCurr - actualMinZ); + // Engines + thrust/fuel use + if (block instanceof IRocketEngine) { + if (block instanceof BlockNuclearRocketMotor) { + nuclearWorkingFluidUseMax += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); + thrustNuclearNozzleLimit += ((IRocketEngine) block).getThrust(world, currPos); + } else if (block instanceof BlockBipropellantRocketMotor) { + bipropellantfuelUse += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); + thrustBipropellant += ((IRocketEngine) block).getThrust(world, currPos); + } else if (block instanceof BlockRocketMotor) { + monopropellantfuelUse += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); + thrustMonopropellant += ((IRocketEngine) block).getThrust(world, currPos); } - if (block instanceof IFuelTank) { - if (block instanceof BlockFuelTank) { - fuelCapacityMonopropellant += (((IFuelTank) block).getMaxFill(world, currPos, state) * ARConfiguration.getCurrentConfig().fuelCapacityMultiplier); - } else if (block instanceof BlockBipropellantFuelTank) { - fuelCapacityBipropellant += (((IFuelTank) block).getMaxFill(world, currPos, state) * ARConfiguration.getCurrentConfig().fuelCapacityMultiplier); - } else if (block instanceof BlockOxidizerFuelTank) { - fuelCapacityOxidizer += (((IFuelTank) block).getMaxFill(world, currPos, state) * ARConfiguration.getCurrentConfig().fuelCapacityMultiplier); - } else if (block instanceof BlockNuclearFuelTank) { - fuelCapacityNuclearWorkingFluid += (((IFuelTank) block).getMaxFill(world, currPos, state) * ARConfiguration.getCurrentConfig().fuelCapacityMultiplier); - } - } + // center engine location for UI/particles + final float halfX = (actualMaxX - actualMinX + 1) / 2f; + final float halfZ = (actualMaxZ - actualMinZ + 1) / 2f; - if (block instanceof IRocketNuclearCore) { - thrustNuclearReactorLimit += ((IRocketNuclearCore) block).getMaxThrust(world, currPos); - } + final float ex = (xCurr - actualMinX + 0.5f) - halfX; + final float ez = (zCurr - actualMinZ + 0.5f) - halfZ; + final float ey = (yCurr - actualMinY); // <- no +0.5 here + + stats.addEngineLocation(ex, ey, ez); + } - if (block instanceof IIntake) { - stats.setStatTag("intakePower", (int) stats.getStatTag("intakePower") + ((IIntake) block).getIntakeAmt(state)); + // Fuel tanks (family-specific capacities) + if (block instanceof IFuelTank) { + if (block instanceof BlockBipropellantFuelTank) { + fuelCapacityBipropellant += ((IFuelTank) block).getMaxFill(world, currPos, state) * ARConfiguration.getCurrentConfig().fuelCapacityMultiplier; + } else if (block instanceof BlockOxidizerFuelTank) { + fuelCapacityOxidizer += ((IFuelTank) block).getMaxFill(world, currPos, state) * ARConfiguration.getCurrentConfig().fuelCapacityMultiplier; + } else if (block instanceof BlockNuclearFuelTank) { + fuelCapacityNuclearWorkingFluid += ((IFuelTank) block).getMaxFill(world, currPos, state) * ARConfiguration.getCurrentConfig().fuelCapacityMultiplier; + } else if (block instanceof BlockFuelTank) { + fuelCapacityMonopropellant += ((IFuelTank) block).getMaxFill(world, currPos, state) * ARConfiguration.getCurrentConfig().fuelCapacityMultiplier; } + } - TileEntity tile = world.getTileEntity(currPos); - IFluidHandler handler; + // Nuclear core limits + if (block instanceof IRocketNuclearCore) { + thrustNuclearReactorLimit += ((IRocketNuclearCore) block).getMaxThrust(world, currPos); + } - if (tile != null && (handler = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null)) != null) { - for (IFluidTankProperties info : handler.getTankProperties()) + // Intakes + if (block instanceof IIntake) { + stats.setStatTag("intakePower", + (int) stats.getStatTag("intakePower") + ((IIntake) block).getIntakeAmt(state)); + } + + // Generic fluid capability presence + capacity + TileEntity tile = world.getTileEntity(currPos); + if (tile != null) { + IFluidHandler handler = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); + if (handler != null) { + for (IFluidTankProperties info : handler.getTankProperties()) { + if (info == null) continue; + if (!foundFluidTank && info.getCapacity() > 0) foundFluidTank = true; fluidCapacity += info.getCapacity(); + } } } } } } + // --- Nuclear working fluid scaling (guarded) --- int nuclearWorkingFluidUse = 0; if (thrustNuclearNozzleLimit > 0) { - //Only run the number of engines our cores can support - we can't throttle these effectively because they're small, so they shut off if they don't get full power thrustNuclearTotalLimit = Math.min(thrustNuclearNozzleLimit, thrustNuclearReactorLimit); - nuclearWorkingFluidUse = (int) (nuclearWorkingFluidUseMax * (thrustNuclearTotalLimit / (float) thrustNuclearNozzleLimit)); - thrustNuclearTotalLimit = (nuclearWorkingFluidUse * thrustNuclearNozzleLimit) / nuclearWorkingFluidUseMax; + if (nuclearWorkingFluidUseMax > 0) { + nuclearWorkingFluidUse = (int) (nuclearWorkingFluidUseMax * (thrustNuclearTotalLimit / (float) thrustNuclearNozzleLimit)); + thrustNuclearTotalLimit = (nuclearWorkingFluidUse * thrustNuclearNozzleLimit) / nuclearWorkingFluidUseMax; + } else { + nuclearWorkingFluidUse = 0; + thrustNuclearTotalLimit = 0; + } } - //Set fuel stats - //Thrust depending on rocket type + // Write stats stats.setBaseFuelRate(FuelType.LIQUID_MONOPROPELLANT, monopropellantfuelUse); - stats.setBaseFuelRate(FuelType.LIQUID_BIPROPELLANT, bipropellantfuelUse); - stats.setBaseFuelRate(FuelType.LIQUID_OXIDIZER, bipropellantfuelUse); + stats.setBaseFuelRate(FuelType.LIQUID_BIPROPELLANT, bipropellantfuelUse); + stats.setBaseFuelRate(FuelType.LIQUID_OXIDIZER, bipropellantfuelUse); stats.setBaseFuelRate(FuelType.NUCLEAR_WORKING_FLUID, nuclearWorkingFluidUse); - //Fuel storage depending on rocket type + + stats.setFuelRate(FuelType.LIQUID_MONOPROPELLANT, monopropellantfuelUse); + stats.setFuelRate(FuelType.LIQUID_BIPROPELLANT, bipropellantfuelUse); + stats.setFuelRate(FuelType.LIQUID_OXIDIZER, bipropellantfuelUse); + stats.setFuelRate(FuelType.NUCLEAR_WORKING_FLUID, nuclearWorkingFluidUse); + stats.setFuelCapacity(FuelType.LIQUID_MONOPROPELLANT, fuelCapacityMonopropellant); - stats.setFuelCapacity(FuelType.LIQUID_BIPROPELLANT, fuelCapacityBipropellant); - stats.setFuelCapacity(FuelType.LIQUID_OXIDIZER, fuelCapacityOxidizer); - stats.setFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID, thrustNuclearTotalLimit); + stats.setFuelCapacity(FuelType.LIQUID_BIPROPELLANT, fuelCapacityBipropellant); + stats.setFuelCapacity(FuelType.LIQUID_OXIDIZER, fuelCapacityOxidizer); + stats.setFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID, fuelCapacityNuclearWorkingFluid); - //Non-fuel stats - stats.setThrust(Math.max(thrustMonopropellant, thrustBipropellant)); - stats.setWeight(numBlocks); + stats.setThrust(Math.max(Math.max(thrustMonopropellant, thrustBipropellant), thrustNuclearTotalLimit)); + stats.setWeight(weight); stats.setStatTag("liquidCapacity", fluidCapacity); - //Total stats, used to check if the user has tried to apply two or more types of thrust/fuel - int totalFuel = fuelCapacityBipropellant + fuelCapacityNuclearWorkingFluid + fuelCapacityMonopropellant; + // Cross-family checks + int totalFuel = fuelCapacityBipropellant + fuelCapacityNuclearWorkingFluid + fuelCapacityMonopropellant; int totalFuelUse = bipropellantfuelUse + nuclearWorkingFluidUse + monopropellantfuelUse; - //Set status - if (invalidBlock) + if (invalidBlock) { status = ErrorCodes.INVALIDBLOCK; - else if (((fuelCapacityBipropellant > 0 && totalFuel > fuelCapacityBipropellant) || (fuelCapacityMonopropellant > 0 && totalFuel > fuelCapacityMonopropellant) || (fuelCapacityNuclearWorkingFluid > 0 && totalFuel > fuelCapacityNuclearWorkingFluid)) || ((thrustBipropellant > 0 && totalFuelUse > bipropellantfuelUse) || (thrustMonopropellant > 0 && totalFuelUse > monopropellantfuelUse) || (thrustNuclearTotalLimit > 0 && totalFuelUse > nuclearWorkingFluidUse))) + } else if (((fuelCapacityBipropellant > 0 && totalFuel > fuelCapacityBipropellant) + || (fuelCapacityMonopropellant > 0 && totalFuel > fuelCapacityMonopropellant) + || (fuelCapacityNuclearWorkingFluid > 0 && totalFuel > fuelCapacityNuclearWorkingFluid)) + || + ((thrustBipropellant > 0 && totalFuelUse > bipropellantfuelUse) + || (thrustMonopropellant > 0 && totalFuelUse > monopropellantfuelUse) + || (thrustNuclearTotalLimit > 0 && totalFuelUse > nuclearWorkingFluidUse))) { status = ErrorCodes.COMBINEDTHRUST; - else if (getThrust() < getNeededThrust()) + } else if (getThrust() <= getNeededThrust()) { status = ErrorCodes.NOENGINES; - else if (((thrustBipropellant > 0) && getFuel(FuelType.LIQUID_BIPROPELLANT) < getNeededFuel(FuelType.LIQUID_BIPROPELLANT)) || ((thrustMonopropellant > 0) && getFuel(FuelType.LIQUID_MONOPROPELLANT) < getNeededFuel(FuelType.LIQUID_MONOPROPELLANT)) || ((thrustNuclearTotalLimit > 0) && getFuel(FuelType.NUCLEAR_WORKING_FLUID) < getNeededFuel(FuelType.NUCLEAR_WORKING_FLUID))) + } else if (((int) stats.getStatTag("intakePower")) <= 0) { + status = ErrorCodes.NOINTAKE; + } else if (!foundFluidTank) { + status = ErrorCodes.NOTANK; + } else if (thrustBipropellant > 0 && (fuelCapacityBipropellant <= 0 || fuelCapacityOxidizer <= 0)) { + status = ErrorCodes.NOFUEL; // missing one of the required tanks + } else if (((thrustBipropellant > 0) && !hasEnoughFuelUnmanned(FuelType.LIQUID_BIPROPELLANT)) + || ((thrustMonopropellant > 0) && !hasEnoughFuelUnmanned(FuelType.LIQUID_MONOPROPELLANT)) + || ((thrustNuclearTotalLimit > 0) && !hasEnoughFuelUnmanned(FuelType.NUCLEAR_WORKING_FLUID))) { status = ErrorCodes.NOFUEL; - else + } else { status = ErrorCodes.SUCCESS; + } } - return new AxisAlignedBB(actualMinX, actualMinY, actualMinZ, actualMaxX, actualMaxY, actualMaxZ); + // Normalize bounds to avoid inverted AABBs on edge cases + double minX = Math.min(actualMinX, actualMaxX); + double minY = Math.min(actualMinY, actualMaxY); + double minZ = Math.min(actualMinZ, actualMaxZ); + double maxX = Math.max(actualMinX, actualMaxX); + double maxY = Math.max(actualMaxY, actualMinY); + double maxZ = Math.max(actualMinZ, actualMaxZ); + return new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); } - public float getNeededFuel(@Nonnull FuelType fuelType) { - return 1; + private boolean hasEnoughFuelUnmanned(@Nonnull FuelType family) { + // SD flight: acceleration in entity code is ≈ 0.005 blocks/tick^2 + final float a_station = 0.005f; + final float targetS = 128f; // SD rocket switches to orbit after ~128 blocks + + float t; // seconds (ticks) we can sustain full burn + + switch (family) { + case LIQUID_MONOPROPELLANT: { + final int cap = stats.getFuelCapacity(FuelType.LIQUID_MONOPROPELLANT); + final int rate = stats.getBaseFuelRate(FuelType.LIQUID_MONOPROPELLANT); + if (cap <= 0 || rate <= 0) return false; + t = cap / (float) rate; + break; + } + + case LIQUID_BIPROPELLANT: { + // Both streams must exist; consume in lockstep at their own rates. + final int capFuel = stats.getFuelCapacity(FuelType.LIQUID_BIPROPELLANT); + final int capOx = stats.getFuelCapacity(FuelType.LIQUID_OXIDIZER); + final int rateFuel= stats.getBaseFuelRate(FuelType.LIQUID_BIPROPELLANT); + final int rateOx = stats.getBaseFuelRate(FuelType.LIQUID_OXIDIZER); + if (capFuel <= 0 || capOx <= 0 || rateFuel <= 0 || rateOx <= 0) return false; + + final float tFuel = capFuel / (float) rateFuel; + final float tOx = capOx / (float) rateOx; + t = Math.min(tFuel, tOx); // limiting stream dictates burn time + break; + } + + case NUCLEAR_WORKING_FLUID: { + final int cap = stats.getFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID); + final int rate = stats.getBaseFuelRate(FuelType.NUCLEAR_WORKING_FLUID); + if (cap <= 0 || rate <= 0) return false; + t = cap / (float) rate; + break; + } + + default: + return false; + } + + // distance under constant accel: s = 0.5 * a * t^2 + final float sCan = 0.5f * a_station * t * t; + return sCan >= targetS; } - //No additional scanning is needed + @Override public void onLoad() { super.onLoad(); } + + @Override public void invalidate() { super.invalidate(); } + + @Override public void onChunkUnload() { super.onChunkUnload(); } + + @Override protected boolean verifyScan(AxisAlignedBB bb, World world) { return true; } -} \ No newline at end of file +} diff --git a/src/main/java/zmaster587/advancedRocketry/tile/atmosphere/TileAtmosphereDetector.java b/src/main/java/zmaster587/advancedRocketry/tile/atmosphere/TileAtmosphereDetector.java index 169933db3..5a01063bf 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/atmosphere/TileAtmosphereDetector.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/atmosphere/TileAtmosphereDetector.java @@ -27,6 +27,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Locale; public class TileAtmosphereDetector extends TileEntity implements ITickable, IModularInventory, IButtonInventory, INetworkMachine { @@ -66,25 +67,52 @@ public boolean shouldRefresh(World world, BlockPos pos, return (oldState.getBlock() != newSate.getBlock()); } + @Override public List getModules(int id, EntityPlayer player) { List modules = new LinkedList<>(); List btns = new LinkedList<>(); - Iterator atmIter = AtmosphereRegister.getInstance().getAtmosphereList().iterator(); + Iterator atmIter = AtmosphereRegister.getInstance() + .getAtmosphereList().iterator(); int i = 0; while (atmIter.hasNext()) { IAtmosphere atm = atmIter.next(); - btns.add(new ModuleButton(60, 4 + i * 24, i, LibVulpes.proxy.getLocalizedString(atm.getUnlocalizedName()), this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); + + // Build a translation key from the internal ID + String key = "msg.atmosphere." + atm.getUnlocalizedName().toLowerCase(Locale.ROOT); + + // Ask for the localized string + String label = LibVulpes.proxy.getLocalizedString(key); + + // If no translation exists, LibVulpes will return the key; + // fall back to the raw ID so nothing breaks. + if (label.equals(key)) { + label = atm.getUnlocalizedName(); + } + + btns.add(new ModuleButton( + 60, + 4 + i * 24, + i, + label, + this, + zmaster587.libVulpes.inventory.TextureResources.buttonBuild + )); i++; } - ModuleContainerPan panningContainer = new ModuleContainerPan(5, 20, btns, new LinkedList<>(), zmaster587.libVulpes.inventory.TextureResources.starryBG, 165, 120, 0, 500); + ModuleContainerPan panningContainer = new ModuleContainerPan( + 5, 20, btns, new LinkedList<>(), + zmaster587.libVulpes.inventory.TextureResources.starryBG, + 160, 100, 0, 500 + ); modules.add(panningContainer); return modules; } + @Override public String getModularInventoryName() { return AdvancedRocketryBlocks.blockOxygenDetection.getLocalizedName(); diff --git a/src/main/java/zmaster587/advancedRocketry/tile/atmosphere/TileGasChargePad.java b/src/main/java/zmaster587/advancedRocketry/tile/atmosphere/TileGasChargePad.java index 503015a99..b5ecf46b3 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/atmosphere/TileGasChargePad.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/atmosphere/TileGasChargePad.java @@ -6,6 +6,8 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.AxisAlignedBB; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; @@ -19,6 +21,7 @@ import zmaster587.libVulpes.tile.TileInventoriedRFConsumerTank; import zmaster587.libVulpes.util.FluidUtils; import zmaster587.libVulpes.util.IconResource; +import zmaster587.libVulpes.cap.TeslaHandler; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -26,10 +29,23 @@ import java.util.List; public class TileGasChargePad extends TileInventoriedRFConsumerTank implements IModularInventory { + private static final int TICK_INTERVAL = 2; + // Avoid per-tick AABB allocation: cache lazily + @Nullable + private AxisAlignedBB cachedPlayerBox; + public TileGasChargePad() { super(0, 2, 16000); } + // Lazy AABB getter + private AxisAlignedBB getPlayerBox() { + if (cachedPlayerBox == null) { + cachedPlayerBox = new AxisAlignedBB(pos, pos.add(1, 2, 1)); + } + return cachedPlayerBox; + } + @Override @Nonnull public int[] getSlotsForFace(@Nullable EnumFacing side) { @@ -51,10 +67,45 @@ public int getPowerPerOperation() { return 0; } + @Override + public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) { + // Hide Forge Energy capability + if (capability == CapabilityEnergy.ENERGY) return false; + // Hide any Tesla capability the base class would expose + if (TeslaHandler.hasTeslaCapability(this, capability)) return false; + return super.hasCapability(capability, facing); + } + + @Override + @Nullable + public T getCapability(Capability capability, @Nullable EnumFacing facing) { + // Don’t provide energy handlers to probes/pipes + if (capability == CapabilityEnergy.ENERGY) return null; + if (TeslaHandler.hasTeslaCapability(this, capability)) return null; + return super.getCapability(capability, facing); + } + + // Optional (extra safety for mods that query IPower-style methods directly) + @Override public boolean canConnectEnergy(EnumFacing side) { return false; } + @Override public boolean canReceive() { return false; } + @Override public int getEnergyStored(EnumFacing side) { return 0; } + @Override public int getMaxEnergyStored(EnumFacing side) { return 0; } + @Override public boolean canPerformFunction() { if (!world.isRemote) { - for (EntityPlayer player : this.world.getEntitiesWithinAABB(EntityPlayer.class, new AxisAlignedBB(pos, pos.add(1, 2, 1)))) { + + // Throttle: only run every TICK_INTERVAL ticks + if ((world.getTotalWorldTime() % TICK_INTERVAL) != 0) { + return false; + } + + FluidStack tf = this.tank.getFluid(); + if (tf == null || tf.amount <= 0) { + return false; + } + + for (EntityPlayer player : this.world.getEntitiesWithinAABB(EntityPlayer.class, getPlayerBox())) { ItemStack stack = player.getItemStackFromSlot(EntityEquipmentSlot.CHEST); if (!stack.isEmpty()) { @@ -67,15 +118,18 @@ else if (ItemAirUtils.INSTANCE.isStackValidAirContainer(stack)) //Check for O2 fill if (fillable != null) { - int amtFluid = fillable.getMaxAir(stack) - fillable.getAirRemaining(stack); - FluidStack fluidStack = this.drain(amtFluid, false); - - if (amtFluid > 0 && fluidStack != null && FluidUtils.areFluidsSameType(fluidStack.getFluid(), AdvancedRocketryFluids.fluidOxygen) && fluidStack.amount > 0) { - FluidStack fstack = this.drain(amtFluid, true); - this.markDirty(); - world.markChunkDirty(getPos(), this); - fillable.increment(stack, fstack.amount); - return true; + int deficit = fillable.getMaxAir(stack) - fillable.getAirRemaining(stack); + tf = this.tank.getFluid(); // refresh + if (deficit > 0 && tf != null + && FluidUtils.areFluidsSameType(tf.getFluid(), AdvancedRocketryFluids.fluidOxygen) + && tf.amount > 0) { + int toDrain = Math.min(deficit, tf.amount); + FluidStack drained = this.drain(toDrain, true); + if (drained != null && drained.amount > 0) { + fillable.increment(stack, drained.amount); + this.markDirty(); // no world.markChunkDirty + return true; + } } } } @@ -85,36 +139,39 @@ else if (ItemAirUtils.INSTANCE.isStackValidAirContainer(stack)) if (this.tank.getFluid() != null && !FluidUtils.areFluidsSameType(this.tank.getFluid().getFluid(), AdvancedRocketryFluids.fluidOxygen) && !stack.isEmpty() && stack.getItem() instanceof IModularArmor) { IInventory inv = ((IModularArmor) stack.getItem()).loadModuleInventory(stack); - FluidStack fluidStack = this.drain(100, false); - if (fluidStack != null) { + // Create a trial FluidStack up to available amount + final int perAttempt = 100 * TICK_INTERVAL; + int trialAmt = Math.min(perAttempt, this.tank.getFluid().amount); + if (trialAmt > 0) { + FluidStack trial = new FluidStack(this.tank.getFluid(), trialAmt); for (int i = 0; i < inv.getSizeInventory(); i++) { if (!((IModularArmor) stack.getItem()).canBeExternallyModified(stack, i)) continue; ItemStack module = inv.getStackInSlot(i); - if (FluidUtils.containsFluid(module)) { - int amtFilled = module.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, EnumFacing.UP).fill(fluidStack, true); - if (amtFilled == 100) { - this.drain(100, true); - - this.markDirty(); - world.markChunkDirty(getPos(), this); - - ((IModularArmor) stack.getItem()).saveModuleInventory(stack, inv); - - return true; - } + if (!FluidUtils.containsFluid(module)) continue; // fast path + net.minecraftforge.fluids.capability.IFluidHandlerItem fh = + module.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, EnumFacing.UP); + if (fh == null) continue; // null-guard + + int amtFilled = fh.fill(trial, true); + // Accept partial fills: drain exactly what was accepted + if (amtFilled > 0) { + this.drain(amtFilled, true); + this.markDirty(); // no world.markChunkDirty + ((IModularArmor) stack.getItem()).saveModuleInventory(stack, inv); + return true; } - } - } - } - - return false; - } - } - return false; - } + } + } + } + } + // no player matched this tick + return false; + } + return false; + } @Override public void performFunction() { @@ -161,4 +218,23 @@ private boolean useBucket(int slot, @Nonnull ItemStack stack) { public boolean isEmpty() { return inventory.isEmpty(); } + + @Override + public void invalidate() { + super.invalidate(); + cachedPlayerBox = null; // drop cached AABB + } + + @Override + public void onChunkUnload() { + super.onChunkUnload(); + cachedPlayerBox = null; // drop cached AABB + } + + @Override + public void onLoad() { + super.onLoad(); + // Ensure cache recomputes from the current pos after NBT load + cachedPlayerBox = null; + } } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/cables/TileWirelessTransciever.java b/src/main/java/zmaster587/advancedRocketry/tile/cables/TileWirelessTransciever.java index 7687b9353..503c2649f 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/cables/TileWirelessTransciever.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/cables/TileWirelessTransciever.java @@ -21,12 +21,13 @@ import zmaster587.advancedRocketry.inventory.TextureResources; import zmaster587.advancedRocketry.world.util.MultiData; import zmaster587.libVulpes.LibVulpes; -import zmaster587.libVulpes.block.RotatableBlock; import zmaster587.libVulpes.interfaces.ILinkableTile; import zmaster587.libVulpes.inventory.modules.IModularInventory; import zmaster587.libVulpes.inventory.modules.IToggleButton; import zmaster587.libVulpes.inventory.modules.ModuleBase; +import zmaster587.libVulpes.inventory.modules.ModuleText; import zmaster587.libVulpes.inventory.modules.ModuleToggleSwitch; +import zmaster587.advancedRocketry.inventory.modules.ModuleWirelessBufferBar; import zmaster587.libVulpes.items.ItemLinker; import zmaster587.libVulpes.network.PacketHandler; import zmaster587.libVulpes.network.PacketMachine; @@ -38,6 +39,30 @@ public class TileWirelessTransciever extends TileEntity implements INetworkMachine, IModularInventory, ILinkableTile, IDataHandler, ITickable, IToggleButton { + + private int transferIntervalTicks = 20; // How often to transfer data (in ticks) + private int phase = -1; // Fixed phase per tile to spread load + private ModuleText netIdLabel; // Show network ID (label) + private final DataStorage uiBuffer = new DataStorage(); // UI-only, never used for logic + + public final DataStorage getUiBufferObject() { + return uiBuffer; + } + + public boolean isLinkedWireless() { + return networkID != -1; + } + + public int getWirelessNetworkId() { + return networkID; + } + + // Avoid per-call allocations from DataType.values() + // needs update if DataType enum changes + private static final DataType[] TYPES = { + DataType.DISTANCE, DataType.HUMIDITY, DataType.TEMPERATURE, + DataType.COMPOSITION, DataType.ATMOSPHEREDENSITY, DataType.MASS + }; protected ModuleToggleSwitch toggleSwitch; boolean extractMode; @@ -51,10 +76,79 @@ public TileWirelessTransciever() { networkID = -1; data = new MultiData(); data.setMaxData(100); + uiBuffer.setMaxData(data.getMaxData()); // UI mirror max should match MultiData max + uiBuffer.setData(0, DataType.UNDEFINED); + syncUiBufferFromMultiData(); toggle = new ModuleToggleSwitch(50, 50, 0, LibVulpes.proxy.getLocalizedString("msg.wirelessTransciever.extract"), this, TextureResources.buttonGeneric, 64, 18, false); toggleSwitch = new ModuleToggleSwitch(160, 5, 1, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonToggleImage, 11, 26, true); + + // Align internal booleans with UI defaults + extractMode = toggle.getState(); // false initially + enabled = toggleSwitch.getState(); // true initially + updateToggleLabel(); + + // Network ID label + String initial = LibVulpes.proxy.getLocalizedString("msg.wirelessTransciever.network") + " -"; + netIdLabel = new ModuleText(40, 72, initial, 0x000000); + netIdLabel.setAlwaysOnTop(true); + } + // Helpers for other terminals to query state + public boolean isEnabledWireless() { + return this.enabled; + } + public boolean isExtractModeWireless() { + return this.extractMode; + } + + // helper for syncing UI buffer from multiData + private void syncUiBufferFromMultiData() { + int total = 0; + int max = data.getMaxData(); + + int nonZero = 0; + DataType lastTypeWithData = DataType.UNDEFINED; + + for (DataType t : TYPES) { + int amt = data.getDataAmount(t); + if (amt > 0) { + nonZero++; + lastTypeWithData = t; + total += amt; + } + } + + if (total < 0) total = 0; + if (total > max) total = max; + + final DataType displayType = (nonZero == 1) ? lastTypeWithData : DataType.UNDEFINED; + + // Mirror into UI-only storage (server-authoritative; GUI module will poll this) + uiBuffer.setMaxData(max); + uiBuffer.setData(total, displayType); + } + + private void updateToggleLabel() { + if (toggle != null) { + String key = extractMode + ? "msg.wirelessTransciever.extract" // pulling from side → network + : "msg.wirelessTransciever.insert"; // pushing from network → side + toggle.setText(LibVulpes.proxy.getLocalizedString(key)); + } + } + + private EnumFacing resolveFront(IBlockState state) { + if (state == null) return EnumFacing.NORTH; + if (state.getBlock() instanceof zmaster587.advancedRocketry.block.BlockTransciever) { + return zmaster587.advancedRocketry.block.BlockTransciever.getFront(state); + } + // fallback for legacy blocks if any remain + if (state.getBlock() instanceof zmaster587.libVulpes.block.RotatableBlock) { + return zmaster587.libVulpes.block.RotatableBlock.getFront(state); + } + return EnumFacing.NORTH; + } @Override public boolean onLinkStart(@Nonnull ItemStack item, TileEntity entity, EntityPlayer player, World world) { @@ -70,15 +164,35 @@ public boolean onLinkStart(@Nonnull ItemStack item, TileEntity entity, EntityPla @Override public void onChunkUnload() { super.onChunkUnload(); - if (NetworkRegistry.dataNetwork.doesNetworkExist(networkID)) + + // Leave the network cleanly if linked + if (networkID != -1 && NetworkRegistry.dataNetwork.doesNetworkExist(networkID)) { NetworkRegistry.dataNetwork.getNetwork(networkID).removeFromAll(this); + } + + // Clear per-instance caches + phase = -1; + + // Clear UI-only mirror (logic stays in 'data') + uiBuffer.setMaxData(data.getMaxData()); + uiBuffer.setData(0, DataType.UNDEFINED); } + @Override public boolean onLinkComplete(@Nonnull ItemStack item, TileEntity entity, EntityPlayer player, World world) { BlockPos pos = ItemLinker.getMasterCoords(item); + if (pos == null) return false; // defensive + + if (pos.equals(this.pos)) { + if (world.isRemote) player.sendMessage(new TextComponentTranslation("msg.linker.sameblock")); + return false; + } + + if (!world.isBlockLoaded(pos)) return false; TileEntity tile = world.getTileEntity(pos); + if (!(tile instanceof TileWirelessTransciever)) return false; if (tile instanceof TileWirelessTransciever) { if (world.isRemote) { @@ -103,6 +217,16 @@ public boolean onLinkComplete(@Nonnull ItemStack item, TileEntity entity, Entity addToNetwork(); ((TileWirelessTransciever) tile).addToNetwork(); + + //SYNC CLIENT UI/STATE FOR BOTH TILES + this.markDirty(); + world.notifyBlockUpdate(this.pos, world.getBlockState(this.pos), world.getBlockState(this.pos), 3); + + tile.markDirty(); + world.notifyBlockUpdate(tile.getPos(), world.getBlockState(tile.getPos()), + world.getBlockState(tile.getPos()), 3); + + ItemLinker.resetPosition(item); return true; @@ -159,10 +283,15 @@ public List getModules(int id, EntityPlayer player) { list.add(toggle); list.add(toggleSwitch); + list.add(netIdLabel); + + // Bar-only UI + list.add(new ModuleWirelessBufferBar(14, 22, uiBuffer)); return list; } + @Override public String getModularInventoryName() { return "tile.wirelessTransciever.name"; @@ -189,134 +318,231 @@ public void readDataFromNetwork(ByteBuf in, byte packetId, } @Override - public void useNetworkData(EntityPlayer player, Side side, byte id, - NBTTagCompound nbt) { - - if (side.isServer()) { - if (id == 0) { - extractMode = nbt.getBoolean("state"); - if (NetworkRegistry.dataNetwork.doesNetworkExist(networkID)) { - NetworkRegistry.dataNetwork.getNetwork(networkID).removeFromAll(this); - - if (extractMode) - NetworkRegistry.dataNetwork.getNetwork(networkID).addSource(this, EnumFacing.UP); - else - NetworkRegistry.dataNetwork.getNetwork(networkID).addSink(this, EnumFacing.UP); + public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompound nbt) { + if (!side.isServer()) return; + + if (id == 1) { // enable/disable + enabled = nbt.getBoolean("state"); + + // persist + push to clients + this.markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + return; + } + + if (id == 0) { // extract/insert + extractMode = nbt.getBoolean("state"); + updateToggleLabel(); + + if (networkID != -1 && NetworkRegistry.dataNetwork.doesNetworkExist(networkID)) { + NetworkRegistry.dataNetwork.getNetwork(networkID).removeFromAll(this); + if (extractMode) { + NetworkRegistry.dataNetwork.getNetwork(networkID).addSource(this, EnumFacing.UP); + } else { + NetworkRegistry.dataNetwork.getNetwork(networkID).addSink(this, EnumFacing.UP); } - } else if (id == 1) { - enabled = nbt.getBoolean("state"); } + + // persist + push to clients + this.markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + return; } } + + @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); extractMode = nbt.getBoolean("mode"); - enabled = nbt.getBoolean("enabled"); - networkID = nbt.getInteger("networkID"); + enabled = nbt.getBoolean("enabled"); + networkID = nbt.getInteger("networkID"); data.readFromNBT(nbt); - //addToNetwork(); - toggle.setToggleState(extractMode); - toggleSwitch.setToggleState(enabled); - } + syncUiBufferFromMultiData(); + + // Mirror persisted booleans into UI widgets (guard null on client) + if (toggle != null) toggle.setToggleState(extractMode); + if (toggleSwitch != null) toggleSwitch.setToggleState(enabled); + updateToggleLabel(); + // Client-only network label text + if (world != null && world.isRemote && netIdLabel != null) { + String idStr = (networkID == -1) + ? net.minecraft.client.resources.I18n.format("msg.wirelessTransciever.network.unlinked") + : Integer.toString(networkID); + String label = LibVulpes.proxy.getLocalizedString("msg.wirelessTransciever.network"); + netIdLabel.setText(label + idStr); + } + } + @Override @Nonnull public NBTTagCompound writeToNBT(NBTTagCompound nbt) { + super.writeToNBT(nbt); nbt.setBoolean("mode", extractMode); nbt.setBoolean("enabled", enabled); nbt.setInteger("networkID", networkID); data.writeToNBT(nbt); - return super.writeToNBT(nbt); + return nbt; } + @Override - public int extractData(int maxAmount, DataType type, EnumFacing dir, - boolean commit) { - return enabled ? data.extractData(maxAmount, type, dir, commit) : 0; + public int extractData(int maxAmount, DataType type, EnumFacing dir, boolean commit) { + int out = enabled ? data.extractData(maxAmount, type, dir, commit) : 0; + if (commit && out > 0) { + syncUiBufferFromMultiData(); + } + return out; } @Override - public int addData(int maxAmount, DataType type, EnumFacing dir, - boolean commit) { - return enabled ? data.addData(maxAmount, type, dir, commit) : 0; + public int addData(int maxAmount, DataType type, EnumFacing dir, boolean commit) { + int in = enabled ? data.addData(maxAmount, type, dir, commit) : 0; + if (commit && in > 0) { + syncUiBufferFromMultiData(); + } + return in; } + @Override public void onLoad() { super.onLoad(); + if (!world.isRemote) { + // Rebuild UI mirror from authoritative MultiData + syncUiBufferFromMultiData(); - if (!NetworkRegistry.dataNetwork.doesNetworkExist(networkID)) - NetworkRegistry.dataNetwork.getNewNetworkID(networkID); + // Mirror persisted booleans -> widgets (do NOT read from widgets) + if (toggle != null) toggle.setToggleState(extractMode); + if (toggleSwitch != null) toggleSwitch.setToggleState(enabled); + updateToggleLabel(); + } - NetworkRegistry.dataNetwork.getNetwork(networkID).removeFromAll(this); + if (world == null || world.isRemote) return; - if (extractMode) + // Recompute throttle phase after we cleared it + if (transferIntervalTicks <= 0) transferIntervalTicks = 20; + phase = (int) Math.floorMod(this.pos.toLong(), transferIntervalTicks); + + // Rejoin the network with the correct role (source/sink) + if (networkID != -1) { + if (!NetworkRegistry.dataNetwork.doesNetworkExist(networkID)) { + NetworkRegistry.dataNetwork.getNewNetworkID(networkID); + } + NetworkRegistry.dataNetwork.getNetwork(networkID).removeFromAll(this); + if (extractMode) { NetworkRegistry.dataNetwork.getNetwork(networkID).addSource(this, EnumFacing.UP); - else + } else { NetworkRegistry.dataNetwork.getNetwork(networkID).addSink(this, EnumFacing.UP); - + } } } + + @Override public void update() { + // Server only + if (world.isRemote) return; - if (!world.isRemote) { - IBlockState state = world.getBlockState(getPos()); - if (state.getBlock() instanceof RotatableBlock) { - EnumFacing facing = RotatableBlock.getFront(state).getOpposite(); - - TileEntity tile = world.getTileEntity(getPos().add(facing.getFrontOffsetX(), facing.getFrontOffsetY(), facing.getFrontOffsetZ())); - - if (tile instanceof IDataHandler && !(tile instanceof TileWirelessTransciever)) { - for (DataType data : DataType.values()) { - - if (data == DataStorage.DataType.UNDEFINED) - continue; - - if (!extractMode) { - int amountCurrent = this.data.getDataAmount(data); - if (amountCurrent > 0) { - int amt = ((IDataHandler) tile).addData(amountCurrent, data, facing.getOpposite(), true); - this.data.extractData(amt, data, facing.getOpposite(), true); - } - } else { - int amt = ((IDataHandler) tile).extractData(this.data.getMaxData() - this.data.getDataAmount(data), data, facing.getOpposite(), true); - this.data.addData(amt, data, facing.getOpposite(), true); - } - } + // Respect front-panel enable switch + if (!enabled) return; + + // Guard against bad values (e.g., NBT edits) + if (transferIntervalTicks <= 0) transferIntervalTicks = 20; + + // Initialize a stable phase to spread load across ticks + if (phase < 0) { + phase = (int) Math.floorMod(this.pos.toLong(), transferIntervalTicks); + } + + // Throttle: only run on the tile's assigned tick + long now = world.getTotalWorldTime(); + if (((now + phase) % transferIntervalTicks) != 0) return; + + IBlockState state = world.getBlockState(getPos()); + + // Resolve front for either 6-way or legacy block + EnumFacing front = resolveFront(state); + EnumFacing facing = front.getOpposite(); + TileEntity neighbor = world.getTileEntity(getPos().offset(facing)); + if (neighbor == null || neighbor instanceof TileWirelessTransciever) return; + if (!(neighbor instanceof IDataHandler)) return; + + boolean changed = false; + + for (DataType dataType : TYPES) { + + if (!extractMode) { + // PUSH: from this buffer -> neighbor + int have = this.data.getDataAmount(dataType); + if (have <= 0) continue; + + int moved = ((IDataHandler) neighbor).addData(have, dataType, facing.getOpposite(), true); + if (moved > 0) { + this.data.extractData(moved, dataType, facing.getOpposite(), true); + changed = true; + } + } else { + // PULL: from neighbor -> this buffer + int room = this.data.getMaxData() - this.data.getDataAmount(dataType); + if (room <= 0) continue; + + int moved = ((IDataHandler) neighbor).extractData(room, dataType, facing.getOpposite(), true); + if (moved > 0) { + this.data.addData(moved, dataType, facing.getOpposite(), true); + changed = true; } } } + + + // Persist changes; a full block update isn't strictly required each tick + if (changed) { + this.markDirty(); + syncUiBufferFromMultiData(); + } } @Override public void onInventoryButtonPressed(int buttonId) { - if (buttonId == 1) + if (buttonId == 1) { enabled = toggleSwitch.getState(); - else if (buttonId == 0) + } else if (buttonId == 0) { extractMode = toggle.getState(); + updateToggleLabel(); + } PacketHandler.sendToServer(new PacketMachine(this, (byte) buttonId)); } - @Override public void stateUpdated(ModuleBase module) { - if (module == toggleSwitch) + if (module == toggleSwitch) { enabled = toggleSwitch.getState(); - else if (module == toggle) + } else if (module == toggle) { extractMode = toggle.getState(); + updateToggleLabel(); + } if (!world.isRemote) { this.markDirty(); world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); } } + @Override + public void invalidate() { + if (!world.isRemote && networkID != -1 && NetworkRegistry.dataNetwork.doesNetworkExist(networkID)) { + NetworkRegistry.dataNetwork.getNetwork(networkID).removeFromAll(this); + } + // Clear per-instance caches + phase = -1; + super.invalidate(); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/hatch/TileDataBus.java b/src/main/java/zmaster587/advancedRocketry/tile/hatch/TileDataBus.java index 41d989503..5980e6593 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/hatch/TileDataBus.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/hatch/TileDataBus.java @@ -9,6 +9,7 @@ import zmaster587.advancedRocketry.api.DataStorage; import zmaster587.advancedRocketry.api.DataStorage.DataType; import zmaster587.advancedRocketry.inventory.modules.ModuleAutoData; +import zmaster587.advancedRocketry.item.IDataItem; import zmaster587.advancedRocketry.item.ItemData; import zmaster587.advancedRocketry.util.IDataInventory; import zmaster587.libVulpes.inventory.modules.ModuleBase; @@ -22,17 +23,18 @@ public class TileDataBus extends TileInventoryHatch implements IDataInventory, INetworkMachine { - DataStorage data; + protected DataStorage data; + protected static final int BASE_MAX_DATA = 2000; public TileDataBus() { data = new DataStorage(DataStorage.DataType.UNDEFINED); - data.setMaxData(2000); + data.setMaxData(BASE_MAX_DATA); } public TileDataBus(int number) { super(number); data = new DataStorage(DataStorage.DataType.UNDEFINED); - data.setMaxData(2000); + data.setMaxData(BASE_MAX_DATA); inventory.setCanInsertSlot(0, true); inventory.setCanInsertSlot(1, false); @@ -45,14 +47,25 @@ public void loadData(int id) { ItemStack itemStack = inventory.getStackInSlot(0); - if (itemStack != ItemStack.EMPTY && itemStack.getItem() instanceof ItemData) { - ItemData itemData = (ItemData) itemStack.getItem(); - itemData.removeData(itemStack, this.data.addData(itemData.getData(itemStack), itemData.getDataType(itemStack), true), DataStorage.DataType.UNDEFINED); + if (!itemStack.isEmpty() && itemStack.getItem() instanceof IDataItem) { + IDataItem item = (IDataItem) itemStack.getItem(); + + DataStorage chip = item.getDataStorage(itemStack); + + int moved = this.data.addData( + chip.getData(), + chip.getDataType(), + true + ); + + // Remove exactly what was accepted + item.removeData(itemStack, moved, DataStorage.DataType.UNDEFINED); inventory.setInventorySlotContents(1, decrStackSize(0, 1)); } } + @Override public String getModularInventoryName() { return "tile.loader.0.name"; @@ -62,9 +75,19 @@ public String getModularInventoryName() { public void storeData(int id) { ItemStack itemStack = inventory.getStackInSlot(0); - if (!itemStack.isEmpty() && itemStack.getItem() instanceof ItemData && inventory.getStackInSlot(1) == ItemStack.EMPTY) { - ItemData itemData = (ItemData) itemStack.getItem(); - this.data.removeData(itemData.addData(itemStack, this.data.getData(), this.data.getDataType()), true); + if (!itemStack.isEmpty() + && itemStack.getItem() instanceof IDataItem + && inventory.getStackInSlot(1).isEmpty()) { + + IDataItem item = (IDataItem) itemStack.getItem(); + + int added = item.addData( + itemStack, + this.data.getData(), + this.data.getDataType() + ); + + this.data.removeData(added, true); inventory.setInventorySlotContents(1, decrStackSize(0, 1)); } @@ -126,16 +149,29 @@ public void readFromNBT(NBTTagCompound nbt) { @Override public void setInventorySlotContents(int slot, @Nonnull ItemStack stack) { inventory.setInventorySlotContents(slot, stack); + ItemStack itemStack = inventory.getStackInSlot(0); - if (itemStack != ItemStack.EMPTY && itemStack.getItem() instanceof ItemData && inventory.getStackInSlot(1) == ItemStack.EMPTY) { - ItemData itemData = (ItemData) itemStack.getItem(); - if (itemData.getData(itemStack) > 0 && data.getData() != data.getMaxData()) { + if (!itemStack.isEmpty() + && itemStack.getItem() instanceof IDataItem + && inventory.getStackInSlot(1).isEmpty()) { + + IDataItem item = (IDataItem) itemStack.getItem(); + DataStorage chip = item.getDataStorage(itemStack); + + int chipData = chip.getData(); + int chipMax = chip.getMaxData(); + + // Auto-load from chip -> bus + if (chipData > 0 && data.getData() < data.getMaxData()) { loadData(0); - } else if (data.getData() != 0 && 1000 > itemData.getData(itemStack)) { + + // Auto-store from bus -> chip + } else if (data.getData() > 0 && chipData < chipMax) { storeData(0); } } + inventory.markDirty(); markDirty(); this.handleUpdateTag(getUpdateTag()); @@ -144,6 +180,11 @@ public void setInventorySlotContents(int slot, @Nonnull ItemStack stack) { ((TileMultiBlock) this.getMasterBlock()).onInventoryUpdated(); } + @Override + public int getInventoryStackLimit() { + return 1; + } + @Override public boolean canExtractItem(int index, @Nonnull ItemStack stack, EnumFacing direction) { return index == 1; diff --git a/src/main/java/zmaster587/advancedRocketry/tile/hatch/TileDataBusBig.java b/src/main/java/zmaster587/advancedRocketry/tile/hatch/TileDataBusBig.java new file mode 100644 index 000000000..e1d0dc349 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/tile/hatch/TileDataBusBig.java @@ -0,0 +1,59 @@ +package zmaster587.advancedRocketry.tile.hatch; + +import net.minecraft.nbt.NBTTagCompound; +import zmaster587.advancedRocketry.api.ARConfiguration; + +public class TileDataBusBig extends TileDataBus { + + private static final int DEFAULT_MULT = 4; + + public TileDataBusBig() { + super(); + enforceBigCapacity(); + } + + public TileDataBusBig(int number) { + super(number); + enforceBigCapacity(); + } + + private static int getConfiguredMultSafe() { + int mult = DEFAULT_MULT; + + try { + ARConfiguration cfg = ARConfiguration.getCurrentConfig(); + if (cfg != null) mult = cfg.dataBusBigMultiplier; + } catch (Throwable ignored) { + // If config isn't ready for any reason, fall back to default. + } + + if (mult < 1) mult = 1; + else if (mult > 20) mult = 20; + + return mult; + } + + private void enforceBigCapacity() { + int mult = getConfiguredMultSafe(); + + long maxLong = (long) BASE_MAX_DATA * (long) mult; + int max = maxLong > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxLong; + + this.data.setMaxData(max); + + if (this.data.getData() > max) { + this.data.setData(max, this.data.getDataType()); + } + } + + @Override + public String getModularInventoryName() { + return "tile.databusbig.name"; + } + + @Override + protected void readFromNBTHelper(NBTTagCompound nbtTagCompound) { + super.readFromNBTHelper(nbtTagCompound); + enforceBigCapacity(); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileFuelingStation.java b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileFuelingStation.java index 70a77e210..8733f8fd5 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileFuelingStation.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileFuelingStation.java @@ -17,7 +17,10 @@ import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidTankProperties; import net.minecraftforge.fml.relauncher.Side; +import zmaster587.advancedRocketry.AdvancedRocketry; import zmaster587.advancedRocketry.api.*; import zmaster587.advancedRocketry.api.fuel.FuelRegistry; import zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType; @@ -41,13 +44,28 @@ import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class TileFuelingStation extends TileInventoriedRFConsumerTank implements IModularInventory, IMultiblock, IInfrastructure, ILinkableTile, INetworkMachine, IButtonInventory { + private EntityRocketBase linkedRocket; private HashedBlockPosition masterBlock; private ModuleRedstoneOutputButton redstoneControl; private RedstoneState state; + // Tune cadence: Ticks between operations + private static final int OP_THROTTLE_TICKS = 5; + + // Stop polling after full for current link/fluid + private boolean fuelingActive = false; + + // Cache last emitted redstone to avoid duplicate updates + private Boolean lastRs = null; + + // Cache resolved fluids from rocket stats + private String lastFuelStr = null, lastOxStr = null, lastWorkStr = null; + private Fluid cachedFuelFluid = null, cachedOxFluid = null, cachedWorkFluid = null; + public TileFuelingStation() { super(1000, 3, 5000); masterBlock = new HashedBlockPosition(0, -1, 0); @@ -55,61 +73,268 @@ public TileFuelingStation() { state = RedstoneState.ON; } - @Override - public int getMaxLinkDistance() { - return 10; + private void syncTE() { + markDirty(); + net.minecraft.block.state.IBlockState s = world.getBlockState(pos); + world.notifyBlockUpdate(pos, s, s, 3); } + @Override + public int getMaxLinkDistance() { return 10; } + + // ---- redstone emission with duplicate suppression ---- private void setRedstoneState(boolean condition) { - if (state == RedstoneState.INVERTED) - condition = !condition; - else if (state == RedstoneState.OFF) - condition = false; - ((BlockTileRedstoneEmitter) AdvancedRocketryBlocks.blockFuelingStation).setRedstoneState(world, world.getBlockState(pos), pos, condition); + if (world == null || world.isRemote) return; + + if (state == RedstoneState.INVERTED) condition = !condition; + else if (state == RedstoneState.OFF) condition = false; + + if (lastRs != null && lastRs == condition) return; + lastRs = condition; + net.minecraft.block.state.IBlockState s = world.getBlockState(pos); + if (AdvancedRocketryBlocks.blockFuelingStation instanceof BlockTileRedstoneEmitter) { + ((BlockTileRedstoneEmitter) AdvancedRocketryBlocks.blockFuelingStation) + .setRedstoneState(world, s, pos, condition); + } + markDirty(); } + + // ---- small cache to avoid repeated FluidRegistry lookups per tick ---- + private void refreshFluidCachesIfNeeded() { + if (linkedRocket == null || linkedRocket.stats == null) return; + String f = linkedRocket.stats.getFuelFluid(); + String o = linkedRocket.stats.getOxidizerFluid(); + String w = linkedRocket.stats.getWorkingFluid(); + + if (!Objects.equals(f, lastFuelStr)) { + lastFuelStr = f; + cachedFuelFluid = (f == null || "null".equals(f) || f.isEmpty()) ? null : FluidRegistry.getFluid(f); + } + if (!Objects.equals(o, lastOxStr)) { + lastOxStr = o; + cachedOxFluid = (o == null || "null".equals(o) || o.isEmpty()) ? null : FluidRegistry.getFluid(o); + } + if (!Objects.equals(w, lastWorkStr)) { + lastWorkStr = w; + cachedWorkFluid = (w == null || "null".equals(w) || w.isEmpty()) ? null : FluidRegistry.getFluid(w); + } + } + + private boolean isStationFluidForThisRocket(Fluid current) { + refreshFluidCachesIfNeeded(); + if (current == null || linkedRocket == null) return false; + + // --- compare by fluid name, not by instance --- + final String currentName = current.getName(); + + // If a specific fluid was already chosen, match directly by name. + if (lastFuelStr != null && !"null".equals(lastFuelStr) && !lastFuelStr.isEmpty() && currentName.equals(lastFuelStr)) { + return true; + } + if (lastOxStr != null && !"null".equals(lastOxStr) && !lastOxStr.isEmpty() && currentName.equals(lastOxStr)) { + return true; + } + if (lastWorkStr != null && !"null".equals(lastWorkStr) && !lastWorkStr.isEmpty() && currentName.equals(lastWorkStr)) { + return true; + } + + // Allow first-time lock-in when rocket hasn't chosen a fluid yet, + // but DOES have capacity for the corresponding tank and "current" is valid for that type. + if ("null".equals(linkedRocket.stats.getFuelFluid())) { + if ((linkedRocket.getFuelCapacity(FuelType.LIQUID_MONOPROPELLANT) > 0 && + FuelRegistry.instance.isFuel(FuelType.LIQUID_MONOPROPELLANT, current)) || + (linkedRocket.getFuelCapacity(FuelType.LIQUID_BIPROPELLANT) > 0 && + FuelRegistry.instance.isFuel(FuelType.LIQUID_BIPROPELLANT, current))) { + return true; + } + } + + if ("null".equals(linkedRocket.stats.getOxidizerFluid())) { + if (linkedRocket.getFuelCapacity(FuelType.LIQUID_OXIDIZER) > 0 && + FuelRegistry.instance.isFuel(FuelType.LIQUID_OXIDIZER, current)) { + return true; + } + } + + if ("null".equals(linkedRocket.stats.getWorkingFluid())) { + if (linkedRocket.getFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID) > 0 && + FuelRegistry.instance.isFuel(FuelType.NUCLEAR_WORKING_FLUID, current)) { + return true; + } + } + + return false; + } + + + @Override + public void update() { + if (world.isRemote) return; + + // Lightweight bucket poll every 10 ticks (automation/hoppers) + if ((world.getTotalWorldTime() % 10L) == 0L) { + ItemStack in = inventory.getStackInSlot(0); + if (!in.isEmpty() && useBucket(0, in)) { + syncTE(); // only when something actually changed + } + } + + super.update(); // IMPORTANT: preserve parent RF/ticking pipeline + } + + @Override public void performFunction() { - if (!world.isRemote) { - //Lock rocket to a specific fluid so that it has only one oxidizer/bipropellant/monopropellant/etc - FluidStack currentFluidStack = tank.getFluid(); - if (currentFluidStack != null) { - Fluid currentFluid = currentFluidStack.getFluid(); - - //Check to see if we should set the rocket fuel - if (linkedRocket.stats.getFuelFluid().equals("null")) { - if ((FuelRegistry.instance.isFuel(FuelType.LIQUID_MONOPROPELLANT, currentFluid) && linkedRocket.getFuelCapacity(FuelType.LIQUID_MONOPROPELLANT) > 0) || (FuelRegistry.instance.isFuel(FuelType.LIQUID_BIPROPELLANT, currentFluid) && linkedRocket.getFuelCapacity(FuelType.LIQUID_BIPROPELLANT) > 0)) - linkedRocket.stats.setFuelFluid(currentFluid.getName()); - } - if (linkedRocket.stats.getOxidizerFluid().equals("null")) { - if (FuelRegistry.instance.isFuel(FuelType.LIQUID_OXIDIZER, currentFluid)) - linkedRocket.stats.setOxidizerFluid(currentFluid.getName()); - } - if (linkedRocket.stats.getWorkingFluid().equals("null")) { - if (FuelRegistry.instance.isFuel(FuelType.NUCLEAR_WORKING_FLUID, currentFluid)) - linkedRocket.stats.setWorkingFluid(currentFluid.getName()); - } + if (world.isRemote) return; // server-only + + if (!fuelingActive) { + FluidStack fs = tank.getFluid(); + boolean relevant = false, room = false; + + if (linkedRocket != null && fs != null) { + Fluid f = fs.getFluid(); + // relevant if this fluid matches what this rocket can actually use + relevant = isStationFluidForThisRocket(f); + // room if the matching logical tank has capacity + room = relevant && canRocketFitFluid(f); + } - //Actually fill the fuel if that is the case - if (currentFluid == FluidRegistry.getFluid(linkedRocket.stats.getFuelFluid()) || currentFluid == FluidRegistry.getFluid(linkedRocket.stats.getOxidizerFluid()) || currentFluid == FluidRegistry.getFluid(linkedRocket.stats.getWorkingFluid())) { - if (linkedRocket.getRocketFuelType() == FuelType.LIQUID_BIPROPELLANT && FuelRegistry.instance.isFuel(FuelType.LIQUID_OXIDIZER, currentFluid)) { - int fuelRate = (int) (FuelRegistry.instance.getMultiplier(FuelType.LIQUID_OXIDIZER, currentFluid) * linkedRocket.stats.getBaseFuelRate(FuelType.LIQUID_OXIDIZER)); - tank.drain(linkedRocket.addFuelAmount(FuelType.LIQUID_OXIDIZER, ARConfiguration.getCurrentConfig().fuelPointsPer10Mb), true); - linkedRocket.setFuelConsumptionRate(FuelType.LIQUID_OXIDIZER, fuelRate); - } else { - int fuelRate = (int) (FuelRegistry.instance.getMultiplier(linkedRocket.getRocketFuelType(), currentFluid) * linkedRocket.stats.getBaseFuelRate(linkedRocket.getRocketFuelType())); - tank.drain(linkedRocket.addFuelAmount(linkedRocket.getRocketFuelType(), ARConfiguration.getCurrentConfig().fuelPointsPer10Mb), true); - linkedRocket.setFuelConsumptionRate(linkedRocket.getRocketFuelType(), fuelRate); - } + setRedstoneState(relevant && !room); // emit when relevant but full + fuelingActive = room; // arm when relevant and there’s room + if (!fuelingActive) return; + } - } + // from here: only do rocket-facing work when it's worth it... + if (linkedRocket == null) { + fuelingActive = false; + setRedstoneState(false); + return; + } + + // Stop all work once full (until unlink/relink) + if (!fuelingActive) { + FluidStack fs = tank.getFluid(); + if (fs != null && canRocketFitFluid(fs.getFluid())) { + fuelingActive = true; // resume fueling after reload + // don’t run expensive work this tick; we’ll catch it on the next throttled pass + } else { + // already full or no relevant fluid — keep RS accurate + setRedstoneState(fs != null && isStationFluidForThisRocket(fs.getFluid()) && !canRocketFitFluid(fs.getFluid())); + } + return; + } + + // Throttle only the expensive fueling/redstone path + if ((world.getTotalWorldTime() % OP_THROTTLE_TICKS) != 0L) return; + + FluidStack currentFluidStack = tank.getFluid(); + if (currentFluidStack == null) { + // No fluid to offer; keep fuelingActive so we’ll retry when fluid arrives + setRedstoneState(false); + return; + } + + final Fluid currentFluid = currentFluidStack.getFluid(); + + // Lock rocket to specific fluids if unset + if ("null".equals(linkedRocket.stats.getFuelFluid())) { + if ((FuelRegistry.instance.isFuel(FuelType.LIQUID_MONOPROPELLANT, currentFluid) && linkedRocket.getFuelCapacity(FuelType.LIQUID_MONOPROPELLANT) > 0) + || (FuelRegistry.instance.isFuel(FuelType.LIQUID_BIPROPELLANT, currentFluid) && linkedRocket.getFuelCapacity(FuelType.LIQUID_BIPROPELLANT) > 0)) { + linkedRocket.stats.setFuelFluid(currentFluid.getName()); + } + } + if ("null".equals(linkedRocket.stats.getOxidizerFluid())) { + if (FuelRegistry.instance.isFuel(FuelType.LIQUID_OXIDIZER, currentFluid)) { + linkedRocket.stats.setOxidizerFluid(currentFluid.getName()); + } + } + if ("null".equals(linkedRocket.stats.getWorkingFluid())) { + if (FuelRegistry.instance.isFuel(FuelType.NUCLEAR_WORKING_FLUID, currentFluid)) { + linkedRocket.stats.setWorkingFluid(currentFluid.getName()); + } + } + + // Update caches after potential stat change + refreshFluidCachesIfNeeded(); + + // If station fluid isn't relevant for this rocket, we can't help + if (!isStationFluidForThisRocket(currentFluid)) { + setRedstoneState(false); + fuelingActive = false; // go fully idle if station fluid not relevant + return; + } + + if (!canRocketFitFluid(currentFluid)) { + setRedstoneState(true); + fuelingActive = false; // early-return above- next pass + markDirty(); + return; + } - //If the rocket is full then emit redstone - setRedstoneState(!canRocketFitFluid(currentFluid)); + // Determine which tank to fill + final FuelType typeToFill; + if (FuelRegistry.instance.isFuel(FuelType.LIQUID_OXIDIZER, currentFluid) + && linkedRocket.getFuelCapacity(FuelType.LIQUID_OXIDIZER) > linkedRocket.getFuelAmount(FuelType.LIQUID_OXIDIZER)) { + typeToFill = FuelType.LIQUID_OXIDIZER; + + } else if (FuelRegistry.instance.isFuel(FuelType.LIQUID_BIPROPELLANT, currentFluid) + && linkedRocket.getFuelCapacity(FuelType.LIQUID_BIPROPELLANT) > linkedRocket.getFuelAmount(FuelType.LIQUID_BIPROPELLANT)) { + typeToFill = FuelType.LIQUID_BIPROPELLANT; + + } else if (FuelRegistry.instance.isFuel(FuelType.LIQUID_MONOPROPELLANT, currentFluid) + && linkedRocket.getFuelCapacity(FuelType.LIQUID_MONOPROPELLANT) > linkedRocket.getFuelAmount(FuelType.LIQUID_MONOPROPELLANT)) { + typeToFill = FuelType.LIQUID_MONOPROPELLANT; + + } else if (FuelRegistry.instance.isFuel(FuelType.NUCLEAR_WORKING_FLUID, currentFluid) + && linkedRocket.getFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID) > linkedRocket.getFuelAmount(FuelType.NUCLEAR_WORKING_FLUID)) { + typeToFill = FuelType.NUCLEAR_WORKING_FLUID; + + } else { + // not relevant or no room + setRedstoneState(false); + fuelingActive = false; + return; + } + + // Bounded transfer scaled by throttle; drain exactly the delta that actually landed + int step = ARConfiguration.getCurrentConfig().fuelPointsPer10Mb; + int toOffer = Math.min(step * OP_THROTTLE_TICKS, tank.getFluidAmount()); + if (toOffer > 0) { + final int before = linkedRocket.getFuelAmount(typeToFill); + final int ret = linkedRocket.addFuelAmount(typeToFill, toOffer); + + // Be robust to either contract: + // - if ret == new total -> delta = ret - before + // - if ret == accepted -> delta = ret (clamped to toOffer) + int delta = Math.max(0, ret - before); + if (delta == 0) { + // Assume ret is "accepted amount" + delta = Math.min(toOffer, Math.max(0, ret)); + } + + if (delta > 0) { + tank.drain(delta, true); + + int baseRate = linkedRocket.stats.getBaseFuelRate(typeToFill); + if (baseRate > 0) { + int multRate = (int)(FuelRegistry.instance.getMultiplier(typeToFill, currentFluid) * baseRate); + if (multRate > 0) { + linkedRocket.setFuelConsumptionRate(typeToFill, multRate); + } + } } } - useBucket(0, inventory.getStackInSlot(0)); + + + // Re-evaluate full; if full now, stop within this link + boolean fullNow = !canRocketFitFluid(currentFluid); + setRedstoneState(fullNow); + if (fullNow) { + fuelingActive = false; + markDirty(); + } } @Override @@ -117,6 +342,7 @@ public int getPowerPerOperation() { return 30; } + @Override public SPacketUpdateTileEntity getUpdatePacket() { return new SPacketUpdateTileEntity(pos, getBlockMetadata(), getUpdateTag()); @@ -132,45 +358,75 @@ public NBTTagCompound getUpdateTag() { return writeToNBT(new NBTTagCompound()); } - @Override public boolean canPerformFunction() { - boolean v = linkedRocket != null && (tank.getFluid() != null && tank.getFluidAmount() > 9 && canRocketFitFluid(tank.getFluid().getFluid())); - //System.out.println(v); - return v; + if (world.isRemote) return false; + if (linkedRocket == null) return false; + + FluidStack fs = tank.getFluid(); + if (fs == null || fs.amount <= 9) return false; + + // Only draw power when the rocket can actually take this fluid + return canRocketFitFluid(fs.getFluid()); } @Override public boolean canFill(Fluid fluid) { - return FuelRegistry.instance.isFuel(FuelType.LIQUID_MONOPROPELLANT, fluid) || FuelRegistry.instance.isFuel(FuelType.NUCLEAR_WORKING_FLUID, fluid) || FuelRegistry.instance.isFuel(FuelType.LIQUID_BIPROPELLANT, fluid) || FuelRegistry.instance.isFuel(FuelType.LIQUID_OXIDIZER, fluid); + if (fluid == null) return false; + return FuelRegistry.instance.isFuel(FuelType.LIQUID_MONOPROPELLANT, fluid) + || FuelRegistry.instance.isFuel(FuelType.NUCLEAR_WORKING_FLUID, fluid) + || FuelRegistry.instance.isFuel(FuelType.LIQUID_BIPROPELLANT, fluid) + || FuelRegistry.instance.isFuel(FuelType.LIQUID_OXIDIZER, fluid); } - /** * @param fluid the fluid to check whether the rocket has space for it * @return boolean on whether the rocket can accept the fluid */ - public boolean canRocketFitFluid(Fluid fluid) { - return canFill(fluid) && ((linkedRocket.getRocketFuelType() == FuelType.LIQUID_BIPROPELLANT && FuelRegistry.instance.isFuel(FuelType.LIQUID_OXIDIZER, fluid)) ? linkedRocket.getFuelCapacity(FuelType.LIQUID_OXIDIZER) > linkedRocket.getFuelAmount(FuelType.LIQUID_OXIDIZER) : linkedRocket.getFuelCapacity(linkedRocket.getRocketFuelType()) > linkedRocket.getFuelAmount(linkedRocket.getRocketFuelType())); - } + private boolean canRocketFitFluid(Fluid f) { + if (f == null || linkedRocket == null) return false; + boolean fits = false; + + // Check every type the fluid qualifies for; OR the results. + if (FuelRegistry.instance.isFuel(FuelType.LIQUID_OXIDIZER, f)) { + fits |= linkedRocket.getFuelAmount(FuelType.LIQUID_OXIDIZER) < linkedRocket.getFuelCapacity(FuelType.LIQUID_OXIDIZER); + } + if (FuelRegistry.instance.isFuel(FuelType.LIQUID_BIPROPELLANT, f)) { + fits |= linkedRocket.getFuelAmount(FuelType.LIQUID_BIPROPELLANT) < linkedRocket.getFuelCapacity(FuelType.LIQUID_BIPROPELLANT); + } + if (FuelRegistry.instance.isFuel(FuelType.LIQUID_MONOPROPELLANT, f)) { + fits |= linkedRocket.getFuelAmount(FuelType.LIQUID_MONOPROPELLANT) < linkedRocket.getFuelCapacity(FuelType.LIQUID_MONOPROPELLANT); + } + if (FuelRegistry.instance.isFuel(FuelType.NUCLEAR_WORKING_FLUID, f)) { + fits |= linkedRocket.getFuelAmount(FuelType.NUCLEAR_WORKING_FLUID) < linkedRocket.getFuelCapacity(FuelType.NUCLEAR_WORKING_FLUID); + } + + return fits; + } @Override public String getModularInventoryName() { return AdvancedRocketryBlocks.blockFuelingStation.getLocalizedName(); } + // keep original claim of custom name, but return non-null to avoid GUI NPEs @Override - public boolean hasCustomName() { - return true; + public boolean hasCustomName() { return true; } + + @Override + public String getName() { + return AdvancedRocketryBlocks.blockFuelingStation.getLocalizedName(); } @Override public void setInventorySlotContents(int slot, @Nonnull ItemStack stack) { - super.setInventorySlotContents(slot, stack); - while (useBucket(0, getStackInSlot(0))) ; - + if (!world.isRemote) { + boolean changed = false; + while (useBucket(0, getStackInSlot(0))) changed = true; // drain all at once + if (changed) syncTE(); // one sync if anything changed + } } /** @@ -184,32 +440,54 @@ private boolean useBucket(int slot, @Nonnull ItemStack stack) { return FluidUtils.attemptDrainContainerIInv(inventory, tank, stack, 0, 1); } + @Override public boolean isItemValidForSlot(int slot, @Nonnull ItemStack stack) { - if (stack.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, EnumFacing.UP)) { - FluidStack fstack = stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, EnumFacing.UP).getTankProperties()[0].getContents(); - return fstack != null && canFill(fstack.getFluid()); - } - return false; + if (!stack.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, EnumFacing.UP)) return false; + IFluidHandler cap = stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, EnumFacing.UP); + if (cap == null) return false; + IFluidTankProperties[] props = cap.getTankProperties(); + if (props == null || props.length == 0) return false; + FluidStack fstack = props[0].getContents(); + return fstack != null && canFill(fstack.getFluid()); } @Override public void unlinkRocket() { this.linkedRocket = null; - ((BlockTileRedstoneEmitter) AdvancedRocketryBlocks.blockFuelingStation).setRedstoneState(world, world.getBlockState(pos), pos, false); - + this.fuelingActive = false; + this.lastRs = null; + lastFuelStr = lastOxStr = lastWorkStr = null; + cachedFuelFluid = cachedOxFluid = cachedWorkFluid = null; + ((BlockTileRedstoneEmitter) AdvancedRocketryBlocks.blockFuelingStation) + .setRedstoneState(world, world.getBlockState(pos), pos, false); + markDirty(); } @Override - public boolean disconnectOnLiftOff() { - return true; - } + public boolean disconnectOnLiftOff() { return true; } @Override public boolean linkRocket(EntityRocketBase rocket) { this.linkedRocket = rocket; - if (tank.getFluid() != null) - setRedstoneState(!canRocketFitFluid(tank.getFluid().getFluid())); + this.lastRs = null; + refreshFluidCachesIfNeeded(); + + boolean room = false; + + if (tank.getFluid() != null) { + Fluid f = tank.getFluid().getFluid(); + boolean relevant = isStationFluidForThisRocket(f); + room = relevant && canRocketFitFluid(f); + setRedstoneState(relevant && !room); + } else { + setRedstoneState(false); + } + + // Arm fueling only if there’s actually room for the current fluid + this.fuelingActive = room; + + syncTE(); return true; } @@ -225,7 +503,9 @@ public boolean onLinkStart(@Nonnull ItemStack item, TileEntity entity, } if (player.world.isRemote) - Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage((new TextComponentString(LibVulpes.proxy.getLocalizedString("msg.fuelingStation.link") + ": " + this.pos.getX() + " " + this.pos.getY() + " " + this.pos.getZ()))); + Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage( + new TextComponentString(LibVulpes.proxy.getLocalizedString("msg.fuelingStation.link") + + ": " + this.pos.getX() + " " + this.pos.getY() + " " + this.pos.getZ())); return true; } @@ -233,18 +513,31 @@ public boolean onLinkStart(@Nonnull ItemStack item, TileEntity entity, public void invalidate() { super.invalidate(); if (getMasterBlock() instanceof TileRocketAssemblingMachine) - ((TileRocketAssemblingMachine) getMasterBlock()).removeConnectedInfrastructure(this); + ((TileRocketAssemblingMachine)getMasterBlock()).removeConnectedInfrastructure(this); - //Mostly for client rendering stuff if (linkedRocket != null) linkedRocket.unlinkInfrastructure(this); + + // Clear caches + lastFuelStr = lastOxStr = lastWorkStr = null; + cachedFuelFluid = cachedOxFluid = cachedWorkFluid = null; + lastRs = null; + fuelingActive = false; + + if (world != null && AdvancedRocketryBlocks.blockFuelingStation instanceof BlockTileRedstoneEmitter) { + ((BlockTileRedstoneEmitter) AdvancedRocketryBlocks.blockFuelingStation) + .setRedstoneState(world, world.getBlockState(pos), pos, false); + } + + markDirty(); } @Override public boolean onLinkComplete(@Nonnull ItemStack item, TileEntity entity, EntityPlayer player, World world) { if (player.world.isRemote) - Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage(new TextComponentTranslation("msg.linker.error.firstMachine")); + Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage( + new TextComponentTranslation("msg.linker.error.firstMachine")); return false; } @@ -273,23 +566,13 @@ public List getModules(int ID, EntityPlayer player) { } @Override - public String getName() { - return null; - } - - @Override - public boolean canInteractWithContainer(EntityPlayer entity) { - return true; - } + public boolean canInteractWithContainer(EntityPlayer entity) { return true; } @Override - public boolean linkMission(IMission mission) { - return false; - } + public boolean linkMission(IMission mission) { return false; } @Override - public void unlinkMission() { - } + public void unlinkMission() { } @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { @@ -298,6 +581,7 @@ public NBTTagCompound writeToNBT(NBTTagCompound nbt) { if (hasMaster()) { nbt.setIntArray("masterPos", new int[]{masterBlock.x, masterBlock.y, masterBlock.z}); } + // fuelingActive not persisted on purpose to match original continuous behavior after reload return nbt; } @@ -311,12 +595,11 @@ public void readFromNBT(NBTTagCompound nbt) { int[] pos = nbt.getIntArray("masterPos"); setMasterBlock(new BlockPos(pos[0], pos[1], pos[2])); } + // lastRs/fuelingActive intentionally not restored; link events will reset as needed } @Override - public boolean hasMaster() { - return masterBlock.y > -1; - } + public boolean hasMaster() { return masterBlock.y > -1; } @Override public TileEntity getMasterBlock() { @@ -324,23 +607,15 @@ public TileEntity getMasterBlock() { } @Override - public void setMasterBlock(BlockPos pos) { - masterBlock = new HashedBlockPosition(pos); - } + public void setMasterBlock(BlockPos pos) { masterBlock = new HashedBlockPosition(pos); } @Override - public void setComplete(BlockPos pos) { - - } + public void setComplete(BlockPos pos) { } @Override - public void setIncomplete() { - masterBlock.y = -1; - } + public void setIncomplete() { masterBlock.y = -1; } - public boolean canRenderConnection() { - return true; - } + public boolean canRenderConnection() { return true; } @Override public void onInventoryButtonPressed(int buttonId) { @@ -354,22 +629,63 @@ public void writeDataToNetwork(ByteBuf out, byte id) { } @Override - public void readDataFromNetwork(ByteBuf in, byte packetId, - NBTTagCompound nbt) { + public void readDataFromNetwork(ByteBuf in, byte packetId, NBTTagCompound nbt) { nbt.setByte("state", in.readByte()); } @Override - public void useNetworkData(EntityPlayer player, Side side, byte id, - NBTTagCompound nbt) { + public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompound nbt) { state = RedstoneState.values()[nbt.getByte("state")]; + markDirty(); + if (side == Side.SERVER && linkedRocket != null) { + FluidStack fs = tank.getFluid(); + if (fs == null) { setRedstoneState(false); return; } + Fluid f = fs.getFluid(); + boolean relevant = isStationFluidForThisRocket(f); + boolean room = relevant && canRocketFitFluid(f); + setRedstoneState(relevant && !room); + } + } + - if (linkedRocket != null && tank.getFluid() != null) - setRedstoneState(!canRocketFitFluid(tank.getFluid().getFluid())); + @Override + public void onLoad() { + if (world.isRemote) return; + lastRs = null; // allow first emit + refreshFluidCachesIfNeeded(); + + boolean emit = false; + if (linkedRocket != null) { + FluidStack fs = tank.getFluid(); + if (fs != null) { + Fluid f = fs.getFluid(); + emit = isStationFluidForThisRocket(f) && !canRocketFitFluid(f); + // also re-arm fueling if we actually can fit + if (!emit && isStationFluidForThisRocket(f) && canRocketFitFluid(f)) { + fuelingActive = true; + } + } + } + setRedstoneState(emit); } @Override - public boolean isEmpty() { - return inventory.isEmpty(); + public void onChunkUnload() { + super.onChunkUnload(); + if (world == null || world.isRemote) return; + + // Clear caches + lastFuelStr = lastOxStr = lastWorkStr = null; + cachedFuelFluid = cachedOxFluid = cachedWorkFluid = null; + lastRs = null; + fuelingActive = false; + if (AdvancedRocketryBlocks.blockFuelingStation instanceof BlockTileRedstoneEmitter) { + ((BlockTileRedstoneEmitter) AdvancedRocketryBlocks.blockFuelingStation) + .setRedstoneState(world, world.getBlockState(pos), pos, false); + } + markDirty(); } + + @Override + public boolean isEmpty() { return inventory.isEmpty(); } } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketFluidLoader.java b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketFluidLoader.java index f035f49cc..6c0331c7b 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketFluidLoader.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketFluidLoader.java @@ -16,6 +16,7 @@ import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.relauncher.Side; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; import zmaster587.advancedRocketry.api.EntityRocketBase; @@ -23,6 +24,7 @@ import zmaster587.advancedRocketry.api.IMission; import zmaster587.advancedRocketry.block.multiblock.BlockARHatch; import zmaster587.advancedRocketry.entity.EntityRocket; +import zmaster587.advancedRocketry.inventory.modules.ModuleSideSelectorTooltipOverlay; import zmaster587.advancedRocketry.tile.TileRocketAssemblingMachine; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.inventory.modules.*; @@ -35,6 +37,8 @@ import zmaster587.libVulpes.util.ZUtils.RedstoneState; import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import java.util.List; public class TileRocketFluidLoader extends TileFluidHatch implements IInfrastructure, ITickable, IButtonInventory, INetworkMachine, IGuiCallback { @@ -45,27 +49,39 @@ public class TileRocketFluidLoader extends TileFluidHatch implements IInfrastruc RedstoneState state; ModuleRedstoneOutputButton inputRedstoneControl; RedstoneState inputstate; + private String[] sideStateNames; ModuleBlockSideSelector sideSelectorModule; + protected static final int TRANSFER_INTERVAL_TICKS = 10; + protected int transferCooldown = 0; public TileRocketFluidLoader() { - redstoneControl = new ModuleRedstoneOutputButton(174, 4, 0, "", this, LibVulpes.proxy.getLocalizedString("msg.fluidLoader.loadingState")); + redstoneControl = new ModuleRedstoneOutputButton(174, 4, 0, "", this, + LibVulpes.proxy.getLocalizedString("msg.fluidLoader.loadingState")); state = RedstoneState.ON; - inputRedstoneControl = new ModuleRedstoneOutputButton(174, 32, 1, "", this, LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowLoading")); + + inputRedstoneControl = new ModuleRedstoneOutputButton(174, 32, 1, "", this, + LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowLoading")); inputstate = RedstoneState.OFF; inputRedstoneControl.setRedstoneState(inputstate); - sideSelectorModule = new ModuleBlockSideSelector(90, 15, this, LibVulpes.proxy.getLocalizedString("msg.fluidLoader.none"), LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowredstoneoutput"), LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowredstoneinput")); + + initSideSelector(); } public TileRocketFluidLoader(int size) { super(size); - redstoneControl = new ModuleRedstoneOutputButton(174, 4, 0, "", this, LibVulpes.proxy.getLocalizedString("msg.fluidLoader.loadingState")); + redstoneControl = new ModuleRedstoneOutputButton(174, 4, 0, "", this, + LibVulpes.proxy.getLocalizedString("msg.fluidLoader.loadingState")); state = RedstoneState.ON; - inputRedstoneControl = new ModuleRedstoneOutputButton(174, 32, 1, "", this, LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowLoading")); + + inputRedstoneControl = new ModuleRedstoneOutputButton(174, 32, 1, "", this, + LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowLoading")); inputstate = RedstoneState.OFF; inputRedstoneControl.setRedstoneState(inputstate); - sideSelectorModule = new ModuleBlockSideSelector(90, 15, this, LibVulpes.proxy.getLocalizedString("msg.fluidLoader.none"), LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowredstoneoutput"), LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowredstoneinput")); + + initSideSelector(); } + @Override public void invalidate() { super.invalidate(); @@ -73,6 +89,15 @@ public void invalidate() { ((TileRocketAssemblingMachine) getMasterBlock()).removeConnectedInfrastructure(this); } + private void initSideSelector() { + sideStateNames = new String[] { + LibVulpes.proxy.getLocalizedString("msg.fluidLoader.none"), + LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowredstoneoutput"), + LibVulpes.proxy.getLocalizedString("msg.fluidLoader.allowredstoneinput") + }; + sideSelectorModule = new ModuleBlockSideSelector(90, 15, this, sideStateNames); + } + @Override public String getModularInventoryName() { return "tile.loader.5.name"; @@ -89,9 +114,15 @@ public List getModules(int ID, EntityPlayer player) { list.add(redstoneControl); list.add(inputRedstoneControl); list.add(sideSelectorModule); + + if (FMLCommonHandler.instance().getSide().isClient()) { + list.add(new ModuleSideSelectorTooltipOverlay(90, 15, sideSelectorModule, sideStateNames)); + } + return list; } + protected boolean getStrongPowerForSides(World world, BlockPos pos) { for (int i = 0; i < 6; i++) { if (sideSelectorModule.getStateForSide(i) == ALLOW_REDSTONEOUT && world.getRedstonePower(pos.offset(EnumFacing.VALUES[i]), EnumFacing.VALUES[i]) > 0) @@ -100,45 +131,112 @@ protected boolean getStrongPowerForSides(World world, BlockPos pos) { return false; } + @Nullable + protected static IFluidHandler getFluidHandlerAnySide(TileEntity te) { + IFluidHandler h = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); + if (h != null) return h; + + for (EnumFacing f : EnumFacing.VALUES) { + h = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, f); + if (h != null) return h; + } + return null; + } + @Nullable + protected static IFluidHandler getBestFillHandler(TileEntity te, @Nonnull FluidStack toInsert) { + // Try null side first + IFluidHandler h = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); + if (h != null) { + FluidStack probe = toInsert.copy(); + if (h.fill(probe, false) > 0) return h; + } + + // Then try all faces + for (EnumFacing f : EnumFacing.VALUES) { + h = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, f); + if (h == null) continue; + + FluidStack probe = toInsert.copy(); + if (h.fill(probe, false) > 0) return h; + } + + return null; + } + + @Nullable + protected static IFluidHandler getBestDrainHandler(TileEntity te) { + // For draining, side rules also exist; "null then faces" is usually OK. + IFluidHandler h = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); + if (h != null) return h; + + for (EnumFacing f : EnumFacing.VALUES) { + h = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, f); + if (h != null) return h; + } + return null; + } + + @Override public void update() { - //Move fluids - if (!world.isRemote && rocket != null) { - - boolean isAllowToOperate = (inputstate == RedstoneState.OFF || isStateActive(inputstate, getStrongPowerForSides(world, getPos()))); - - List tiles = rocket.storage.getFluidTiles(); - boolean rocketFluidFull = false; - - boolean doupdate = false; - //Function returns if something can be moved - for (TileEntity tile : tiles) { - IFluidHandler handler = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); - - //See if we have anything to fill because redstone output - FluidStack rocketFluid = handler.drain(1, false); - if (handler.fill(rocketFluid, false) > 0) - rocketFluidFull = true; - - if (isAllowToOperate) { - rocketFluid = fluidTank.drain(fluidTank.getCapacity(), false); - if (rocketFluid != null && rocketFluid.amount > 0) { - fluidTank.drain(handler.fill(rocketFluid, true), true); - doupdate = true; - } + if (world.isRemote || rocket == null) return; + + if (transferCooldown > 0) { + transferCooldown--; + return; + } + transferCooldown = TRANSFER_INTERVAL_TICKS; + + boolean isAllowToOperate = (inputstate == RedstoneState.OFF + || isStateActive(inputstate, getStrongPowerForSides(world, getPos()))); + + List tiles = rocket.storage.getFluidTiles(); + boolean rocketHasFillCapacitySomewhere = false; + boolean doupdate = false; + + for (TileEntity tile : tiles) { + if (tile == null || tile.isInvalid()) continue; + + IFluidHandler drainHandler = getBestDrainHandler(tile); + if (drainHandler == null) continue; + + // --- redstone probe (keep your current semantics for now) + FluidStack probe = drainHandler.drain(1, false); + if (probe != null && probe.amount > 0) { + IFluidHandler fillProbeHandler = getBestFillHandler(tile, probe); + if (fillProbeHandler != null && fillProbeHandler.fill(probe, false) > 0) { + rocketHasFillCapacitySomewhere = true; } } - if (doupdate) { - PacketHandler.sendToNearby(new PacketEntity(rocket, (byte) 9987), world.provider.getDimension(), getPos(), 128); - } - //Update redstone state - setRedstoneState(!rocketFluidFull); + if (!isAllowToOperate) continue; + + FluidStack fromLoader = fluidTank.drain(fluidTank.getCapacity(), false); + if (fromLoader == null || fromLoader.amount <= 0) continue; + + IFluidHandler fillHandler = getBestFillHandler(tile, fromLoader); + if (fillHandler == null) continue; + + int accepted = fillHandler.fill(fromLoader, true); + if (accepted > 0) { + fluidTank.drain(accepted, true); + doupdate = true; + break; + } + } + if (doupdate) { + PacketHandler.sendToNearby(new PacketEntity(rocket, (byte) 9987), + world.provider.getDimension(), getPos(), 128); + markDirty(); } + + setRedstoneState(!rocketHasFillCapacitySomewhere); } + + @Override public SPacketUpdateTileEntity getUpdatePacket() { return new SPacketUpdateTileEntity(pos, getBlockMetadata(), getUpdateTag()); diff --git a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketFluidUnloader.java b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketFluidUnloader.java index 60529a665..bedab1e5b 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketFluidUnloader.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketFluidUnloader.java @@ -2,6 +2,7 @@ import micdoodle8.mods.galacticraft.core.network.PacketEntityUpdate; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; @@ -16,6 +17,8 @@ import java.util.List; +import javax.annotation.Nullable; + public class TileRocketFluidUnloader extends TileRocketFluidLoader implements IInfrastructure, ITickable, IButtonInventory, INetworkMachine { public TileRocketFluidUnloader() { @@ -32,48 +35,82 @@ public String getModularInventoryName() { return "tile.loader.4.name"; } + @Nullable + private static IFluidHandler getBestDrainHandler(TileEntity te, int probeAmount) { + // Try null side first + IFluidHandler h = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); + if (h != null) { + FluidStack probe = h.drain(probeAmount, false); + if (probe != null && probe.amount > 0) return h; + } + + // Then try all faces + for (EnumFacing f : EnumFacing.VALUES) { + h = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, f); + if (h == null) continue; + + FluidStack probe = h.drain(probeAmount, false); + if (probe != null && probe.amount > 0) return h; + } + + return null; + } @Override public void update() { - //Move fluids - if (!world.isRemote && rocket != null) { - - boolean isAllowToOperate = (inputstate == RedstoneState.OFF || isStateActive(inputstate, getStrongPowerForSides(world, getPos()))); - - List tiles = rocket.storage.getFluidTiles(); - boolean rocketFluidFull = false; - - boolean doupdate = false; - //Function returns if something can be moved - for (TileEntity tile : tiles) { - IFluidHandler handler = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); - - //See if we have anything to fill because redstone output - FluidStack rocketFluid = handler.drain(1, false); - if (handler.fill(rocketFluid, false) > 0) - rocketFluidFull = true; - - if (isAllowToOperate) { - boolean shouldOperate; - if (getFluidTank().getFluid() != null) - shouldOperate = getFluidTank().fill(handler.drain(new FluidStack(getFluidTank().getFluid(), getFluidTank().getCapacity() - getFluidTank().getFluidAmount()), false), false) > 0; - else - shouldOperate = getFluidTank().fill(handler.drain(getFluidTank().getCapacity(), false), false) > 0; - - if (shouldOperate) { - doupdate = true; - getFluidTank().fill(handler.drain(Math.max(50, getFluidTank().getCapacity() - getFluidTank().getFluidAmount()), true), true); - } - } - } - if (doupdate) { - PacketHandler.sendToNearby(new PacketEntity(rocket, (byte) 9987), world.provider.getDimension(), getPos(), 128); + if (world.isRemote || rocket == null) return; + + if (transferCooldown > 0) { + transferCooldown--; + return; + } + transferCooldown = TRANSFER_INTERVAL_TICKS; + + boolean isAllowToOperate = (inputstate == RedstoneState.OFF + || isStateActive(inputstate, getStrongPowerForSides(world, getPos()))); + + List tiles = rocket.storage.getFluidTiles(); + + boolean rocketHasDrainableFluidSomewhere = false; + boolean doupdate = false; + + for (TileEntity tile : tiles) { + if (tile == null || tile.isInvalid()) continue; + + IFluidHandler drainHandler = getBestDrainHandler(tile, 1); + if (drainHandler == null) continue; + + // redstone probe: does rocket have any drainable fluid? + FluidStack probe = drainHandler.drain(1, false); + if (probe != null && probe.amount > 0) { + rocketHasDrainableFluidSomewhere = true; } - //Update redstone state - setRedstoneState(!rocketFluidFull); + if (!isAllowToOperate) continue; + + int space = getFluidTank().getCapacity() - getFluidTank().getFluidAmount(); + if (space <= 0) continue; + + FluidStack simulated = drainHandler.drain(space, false); + if (simulated == null || simulated.amount <= 0) continue; + int accepted = getFluidTank().fill(simulated, false); + if (accepted <= 0) continue; + + FluidStack drained = drainHandler.drain(accepted, true); + if (drained != null && drained.amount > 0) { + getFluidTank().fill(drained, true); + doupdate = true; + break; // one transfer per ticks + } } - } + if (doupdate) { + PacketHandler.sendToNearby(new PacketEntity(rocket, (byte) 9987), + world.provider.getDimension(), getPos(), 128); + markDirty(); + } + + setRedstoneState(!rocketHasDrainableFluidSomewhere); + } } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketLoader.java b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketLoader.java index 689f7bbbd..c9000473d 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketLoader.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketLoader.java @@ -3,7 +3,6 @@ import io.netty.buffer.ByteBuf; import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; @@ -14,16 +13,21 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; +import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.ItemHandlerHelper; +import net.minecraftforge.items.wrapper.InvWrapper; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; import zmaster587.advancedRocketry.api.EntityRocketBase; import zmaster587.advancedRocketry.api.IInfrastructure; import zmaster587.advancedRocketry.api.IMission; import zmaster587.advancedRocketry.block.multiblock.BlockARHatch; import zmaster587.advancedRocketry.entity.EntityRocket; +import zmaster587.advancedRocketry.inventory.modules.ModuleSideSelectorTooltipOverlay; import zmaster587.advancedRocketry.tile.TileGuidanceComputer; +import zmaster587.advancedRocketry.tile.hatch.TileSatelliteHatch; import zmaster587.advancedRocketry.tile.TileRocketAssemblingMachine; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.inventory.modules.*; @@ -39,6 +43,7 @@ public class TileRocketLoader extends TileInventoryHatch implements IInfrastructure, ITickable, IButtonInventory, INetworkMachine, IGuiCallback { + private String[] sideStateNames; private final static int ALLOW_REDSTONEOUT = 2; EntityRocket rocket; ModuleRedstoneOutputButton redstoneControl; @@ -47,13 +52,21 @@ public class TileRocketLoader extends TileInventoryHatch implements IInfrastruct RedstoneState inputstate; ModuleBlockSideSelector sideSelectorModule; + protected static final int TRANSFER_INTERVAL_TICKS = 20; + protected static final int MAX_TRANSFER_PER_OPERATION = 64; + protected int transferCooldown = 0; + + // Own wrapper around the EmbeddedInventory from TileInventoryHatch. + // We DO NOT use the broken capability from LibVulpes for ourselves. + protected final IItemHandler ownItemHandler = new InvWrapper(this.inventory); + public TileRocketLoader() { redstoneControl = new ModuleRedstoneOutputButton(174, 4, 0, "", this, LibVulpes.proxy.getLocalizedString("msg.rocketLoader.loadingState")); state = RedstoneState.ON; inputRedstoneControl = new ModuleRedstoneOutputButton(174, 32, 1, "", this, LibVulpes.proxy.getLocalizedString("msg.rocketLoader.allowLoading")); inputstate = RedstoneState.OFF; inputRedstoneControl.setRedstoneState(inputstate); - sideSelectorModule = new ModuleBlockSideSelector(90, 15, this, LibVulpes.proxy.getLocalizedString("msg.rocketLoader.none"), LibVulpes.proxy.getLocalizedString("msg.rocketLoader.allowredstoneoutput"), LibVulpes.proxy.getLocalizedString("msg.rocketLoader.allowredstoneinput")); + initSideSelector(); } public TileRocketLoader(int size) { @@ -71,8 +84,51 @@ public TileRocketLoader(int size) { inputRedstoneControl = new ModuleRedstoneOutputButton(174, 32, 1, "", this, LibVulpes.proxy.getLocalizedString("msg.rocketLoader.allowLoading")); inputstate = RedstoneState.OFF; inputRedstoneControl.setRedstoneState(inputstate); - sideSelectorModule = new ModuleBlockSideSelector(90, 15, this, LibVulpes.proxy.getLocalizedString("msg.rocketLoader.none"), LibVulpes.proxy.getLocalizedString("msg.rocketLoader.allowredstoneoutput"), LibVulpes.proxy.getLocalizedString("msg.rocketLoader.allowredstoneinput")); + initSideSelector(); + } + + // Used for rocket / other tiles – they SHOULD implement IItemHandler correctly. + protected IItemHandler getItemHandler(TileEntity tile) { + if (tile == null || tile.isInvalid()) + return null; + + // Prefer null side + if (tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)) { + Object cap = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); + if (cap instanceof IItemHandler) { + return (IItemHandler) cap; + } + } + + // Fallback: try all sides + for (EnumFacing side : EnumFacing.values()) { + if (tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)) { + Object cap = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side); + if (cap instanceof IItemHandler) { + return (IItemHandler) cap; + } + } + } + + return null; + } + + + // For THIS tile only: never go through LibVulpes’ capability (it returns EmbeddedInventory). + protected IItemHandler getOwnItemHandler() { + return ownItemHandler; + } + + + private void initSideSelector() { + sideStateNames = new String[] { + LibVulpes.proxy.getLocalizedString("msg.rocketLoader.none"), + LibVulpes.proxy.getLocalizedString("msg.rocketLoader.allowredstoneoutput"), + LibVulpes.proxy.getLocalizedString("msg.rocketLoader.allowredstoneinput") + }; + + sideSelectorModule = new ModuleBlockSideSelector(90, 15, this, sideStateNames); } @Override @@ -98,6 +154,10 @@ public List getModules(int ID, EntityPlayer player) { list.add(redstoneControl); list.add(inputRedstoneControl); list.add(sideSelectorModule); + if (FMLCommonHandler.instance().getSide().isClient()) { + list.add(new ModuleSideSelectorTooltipOverlay(90, 15, sideSelectorModule, sideStateNames)); + } + return list; } @@ -111,94 +171,100 @@ protected boolean getStrongPowerForSides(World world, BlockPos pos) { @Override public void update() { - //Move a stack of items - if (!world.isRemote && rocket != null) { - - boolean isAllowedToOperate = (inputstate == RedstoneState.OFF || isStateActive(inputstate, getStrongPowerForSides(world, getPos()))); - - List tiles = rocket.storage.getInventoryTiles(); - boolean foundStack = false; - boolean rocketContainsItems = false; - out: - //Function returns if something can be moved - for (TileEntity tile : tiles) { - if (tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP)) { - if(tile instanceof TileGuidanceComputer) continue; - - IItemHandler inv = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP); - - for (int i = 0; i < inv.getSlots(); i++) { - if (inv.getStackInSlot(i).isEmpty()) - rocketContainsItems = true; - - //Loop though this inventory's slots and find a suitible one - for (int j = 0; j < getSizeInventory(); j++) { - if ((inv.getStackInSlot(i).isEmpty()) && !inventory.getStackInSlot(j).isEmpty()) { - if (isAllowedToOperate) { - inv.insertItem(i, inventory.getStackInSlot(j), false); - inventory.setInventorySlotContents(j, ItemStack.EMPTY); - } - rocketContainsItems = true; - break out; - } else if (!getStackInSlot(j).isEmpty() && inv.getStackInSlot(i).getItem() == getStackInSlot(j).getItem() && - ItemStack.areItemStackTagsEqual(inv.getStackInSlot(i), getStackInSlot(j)) && inv.getStackInSlot(i).getMaxStackSize() != inv.getStackInSlot(i).getCount()) { - if (isAllowedToOperate) { - ItemStack stack2 = inventory.decrStackSize(j, inv.getStackInSlot(i).getMaxStackSize() - inv.getStackInSlot(i).getCount()); - inv.getStackInSlot(i).setCount(inv.getStackInSlot(i).getCount() + stack2.getCount()); - } - rocketContainsItems = true; - - if (inventory.getStackInSlot(j).isEmpty()) - break out; - - foundStack = true; - } - } - if (foundStack) - break out; - } - } else { - if (tile instanceof IInventory && !(tile instanceof TileGuidanceComputer)) { - IInventory inv = ((IInventory) tile); - - for (int i = 0; i < inv.getSizeInventory(); i++) { - if (inv.getStackInSlot(i).isEmpty()) - rocketContainsItems = true; - - //Loop though this inventory's slots and find a suitible one - for (int j = 0; j < getSizeInventory(); j++) { - if ((inv.getStackInSlot(i).isEmpty()) && !inventory.getStackInSlot(j).isEmpty()) { - if (isAllowedToOperate) { - inv.setInventorySlotContents(i, inventory.getStackInSlot(j)); - inventory.setInventorySlotContents(j, ItemStack.EMPTY); - } - rocketContainsItems = true; - break out; - } else if (!getStackInSlot(j).isEmpty() && inv.isItemValidForSlot(i, getStackInSlot(j)) && inv.getStackInSlot(i).getItem() == getStackInSlot(j).getItem() && - ItemStack.areItemStackTagsEqual(inv.getStackInSlot(i), getStackInSlot(j)) && inv.getStackInSlot(i).getMaxStackSize() != inv.getStackInSlot(i).getCount()) { - if (isAllowedToOperate) { - ItemStack stack2 = inventory.decrStackSize(j, inv.getStackInSlot(i).getMaxStackSize() - inv.getStackInSlot(i).getCount()); - inv.getStackInSlot(i).setCount(inv.getStackInSlot(i).getCount() + stack2.getCount()); - } - rocketContainsItems = true; - - if (inventory.getStackInSlot(j).isEmpty()) - break out; - - foundStack = true; - } - } - if (foundStack) - break out; - } - } + if (world.isRemote || rocket == null) + return; + + // Throttle: only try to move items every TRANSFER_INTERVAL_TICKS + if (transferCooldown > 0) { + transferCooldown--; + return; + } + + boolean isAllowedToOperate = (inputstate == RedstoneState.OFF || + isStateActive(inputstate, getStrongPowerForSides(world, getPos()))); + + IItemHandler ownHandler = getOwnItemHandler(); + if (ownHandler == null || ownHandler.getSlots() == 0) { + // Nothing to move / no handler -> treat as not doing anything + setRedstoneState(false); + return; + } + + List tiles = rocket.storage.getInventoryTiles(); + boolean rocketHasCapacity = false; // true if any slot can still take items + + outer: + for (TileEntity tile : tiles) { + if (tile instanceof TileGuidanceComputer || tile instanceof TileSatelliteHatch) + continue; + + IItemHandler rocketHandler = getItemHandler(tile); + if (rocketHandler == null || rocketHandler.getSlots() == 0) + continue; + + int rocketSlots = rocketHandler.getSlots(); + int ownSlots = ownHandler.getSlots(); + + // Capacity detection for redstone: matches original semantics (any empty slot) + for (int rocketSlot = 0; rocketSlot < rocketSlots; rocketSlot++) { + ItemStack rocketStack = rocketHandler.getStackInSlot(rocketSlot); + if (rocketStack.isEmpty()) { + rocketHasCapacity = true; + break; } } - //Update redstone state - setRedstoneState(!rocketContainsItems); + // If we are not allowed to operate, we only care about capacity for redstone + if (!isAllowedToOperate) + continue; + + // Actual transfer: handler-wide insert using ItemHandlerHelper + for (int ownSlot = 0; ownSlot < ownSlots; ownSlot++) { + ItemStack sourceStack = ownHandler.getStackInSlot(ownSlot); + if (sourceStack.isEmpty()) + continue; + + // Limit per-operation transfer, but DO NOT assume anything about slot max size + int maxToMove = Math.min(MAX_TRANSFER_PER_OPERATION, sourceStack.getCount()); + if (maxToMove <= 0) + continue; + + // Simulate extraction from our inventory + ItemStack simulatedExtract = ownHandler.extractItem(ownSlot, maxToMove, true); + if (simulatedExtract.isEmpty()) + continue; + + // Simulate insertion into the rocket inventory as a whole + ItemStack simulatedRemainder = ItemHandlerHelper.insertItem(rocketHandler, simulatedExtract, true); + int accepted = simulatedExtract.getCount() - simulatedRemainder.getCount(); + if (accepted <= 0) + continue; + + // Actually extract exactly what the rocket said it will accept + ItemStack actuallyExtracted = ownHandler.extractItem(ownSlot, accepted, false); + if (actuallyExtracted.isEmpty()) + continue; + + // Actually insert into rocket + ItemStack remainder = ItemHandlerHelper.insertItem(rocketHandler, actuallyExtracted, false); + + // Normally remainder should be empty because we respected 'accepted'. + // Absolute last-resort fallback for misbehaving handlers: try to put remainder back. + if (!remainder.isEmpty()) { + ItemHandlerHelper.insertItem(ownHandler, remainder, false); + // If this still leaves items, they'll effectively vanish, but only + // in the case of a broken mod that lied during simulation. + } + transferCooldown = TRANSFER_INTERVAL_TICKS; + markDirty(); + tile.markDirty(); + break outer; // only one transfer per operation + } } + + // Redstone: ON when rocketHasCapacity == false (i.e. no empty slot -> "full" rocket) + setRedstoneState(!rocketHasCapacity); } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketMonitoringStation.java b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketMonitoringStation.java index 9ab6b6334..5a29505f1 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketMonitoringStation.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketMonitoringStation.java @@ -5,62 +5,259 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.play.server.SPacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import zmaster587.libVulpes.tile.IMultiblock; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.EntityRocketBase; import zmaster587.advancedRocketry.api.IInfrastructure; import zmaster587.advancedRocketry.api.IMission; +import zmaster587.advancedRocketry.api.RocketEvent; import zmaster587.advancedRocketry.api.fuel.FuelRegistry; import zmaster587.advancedRocketry.api.satellite.SatelliteBase; import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.advancedRocketry.entity.EntityRocket; +import zmaster587.advancedRocketry.entity.EntityStationDeployedRocket; import zmaster587.advancedRocketry.inventory.TextureResources; +import zmaster587.advancedRocketry.tile.TileRocketAssemblingMachine; +import zmaster587.advancedRocketry.tile.TileUnmannedVehicleAssembler; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.client.util.IndicatorBarImage; import zmaster587.libVulpes.client.util.ProgressBarImage; import zmaster587.libVulpes.interfaces.ILinkableTile; import zmaster587.libVulpes.inventory.modules.*; +import zmaster587.libVulpes.inventory.GuiHandler; import zmaster587.libVulpes.items.ItemLinker; import zmaster587.libVulpes.network.PacketHandler; import zmaster587.libVulpes.network.PacketMachine; import zmaster587.libVulpes.tile.IComparatorOverride; +import zmaster587.libVulpes.util.HashedBlockPosition; import zmaster587.libVulpes.util.IAdjBlockUpdate; import zmaster587.libVulpes.util.INetworkMachine; -import zmaster587.libVulpes.util.ZUtils.RedstoneState; import javax.annotation.Nonnull; import java.util.LinkedList; import java.util.List; -public class TileRocketMonitoringStation extends TileEntity implements IModularInventory, ITickable, IAdjBlockUpdate, IInfrastructure, ILinkableTile, INetworkMachine, IButtonInventory, IProgressBar, IComparatorOverride { +public class TileRocketMonitoringStation extends TileEntity + implements IModularInventory, ITickable, IAdjBlockUpdate, IInfrastructure, + ILinkableTile, INetworkMachine, IButtonInventory, IProgressBar, + IComparatorOverride, IGuiCallback, IMultiblock { + + // 2–3 ticks for height/vel feels live; 5–10 ticks is fine for fuel. + private static final int T_HEIGHTVEL_TICKS = 3; // ~6.7 Hz + private static final int T_FUEL_TICKS = 10; // ~2 Hz + private static final int T_COMPARATOR_TICKS = 3; // match height cadence + // ================================= + + // Server-only: assembler-driven claim window + private int expectedRocketId = -1; + private long expectedRocketExpiry = 0L; + + // "this rocket belongs to me" + public void markRocketFromAssembler(EntityRocketBase rocket) { + if (world == null || world.isRemote || rocket == null) return; + this.expectedRocketId = rocket.getEntityId(); + this.expectedRocketExpiry = world.getTotalWorldTime() + 40; // ~2 seconds + } EntityRocketBase linkedRocket; IMission mission; ModuleText missionText; - //RedstoneState state; - //ModuleRedstoneOutputButton redstoneControl; + + // Cached redstone state from neighbor callbacks (don’t poll every tick) + private boolean isPoweredCached = false, initPower = false; + + // Throttles + private int heightVelTick = 0, fuelTick = 0, comparatorTick = 0; + + // Comparator cache (change-only) + private int lastComparator = -1; + + // Server snapshots (served via ModuleProgress polling) + private int snapHeight = 0, snapVel = 0; + private int snapFuel = 0, snapFuelCap = 0; // active fuel (id=2 semantics) + private int snapOx = 0, snapOxCap = 0; // oxidizer (id=6 semantics) + private int lastKnownFuelCap = 0; // active fuel cap (mono/bi/nuclear) + private int lastKnownOxCap = 0; // oxidizer cap + + + // GUI cached fields (client) boolean was_powered = false; int rocketHeight; int velocity; int fuelLevel, maxFuelLevel; int oxidizerFuelLevel; + + // === GUI event status (server -> client via TE update) === + // 0=idle, 1=prelaunch, 2=launching, 3=orbit, 4=deorbiting, 5=landed, 6=aborted + + private int uiStatus = 0; + private transient ModuleText launchStatus; // client-only widget + private transient ModuleText abortDetail; + private transient int lastUiStatusShown = -1; // client change-detect + // How long a status is considered fresh after the last event (in ticks) + private static final long STATUS_STALE_TICKS = 600L; // over 30 seconds is outdated + private long lastStatusTick = 0L; // server-only; persisted + private String lastAbortReason = ""; + + // Tabs (client-only) + private static final byte TAB_SWITCH = 10; + private ModuleTab tabModule; + // Event bus registration flag + private boolean registeredBus = false; + + private void pushState() { + if (world != null && !world.isRemote) { + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + } + } + + private boolean isRocketAllowedForMaster(@Nonnull EntityRocketBase rocket) { + // if something is weird, don't block linking + if (world == null || rocket == null) { + return true; + } + + // Free-floating monitor with no master: no restriction + if (!hasMaster()) { + return true; + } + + TileEntity master = getMasterBlock(); + if (!(master instanceof TileUnmannedVehicleAssembler)) { + // Master is some other assembler type: no SD-only restriction + return true; + } + + // From here: this monitor is owned by an *unmanned* vehicle assembler. + // Only accept SD rockets. + if (rocket instanceof EntityStationDeployedRocket) { + return true; + } + + // Anything else is not allowed for this master + return false; + } + + private void clearUiStatus() { + uiStatus = 0; + lastAbortReason = ""; + lastUiStatusShown = -1; // force client label to refresh to empty + pushState(); + } + public TileRocketMonitoringStation() { mission = null; - missionText = new ModuleText(20, 90, LibVulpes.proxy.getLocalizedString("msg.monitoringStation.missionProgressNA"), 0x2b2b2b); - //redstoneControl = new ModuleRedstoneOutputButton(174, 4, -1, "", this); - //state = RedstoneState.ON; + missionText = null; + + tabModule = new ModuleTab( + 4, 0, 0, this, 2, + new String[] { + LibVulpes.proxy.getLocalizedString("msg.monitoringStation.tab.status"), + LibVulpes.proxy.getLocalizedString("msg.monitoringStation.tab.mission") + }, + new net.minecraft.util.ResourceLocation[][] { + TextureResources.tabPlanet, + TextureResources.tabPlanetTracking + } + ); + } + // --- Master / assembler association --- + private HashedBlockPosition masterBlock = new HashedBlockPosition(0, -1, 0); + + @Override + public boolean hasMaster() { + return masterBlock.y > -1; + } + + @Override + public TileEntity getMasterBlock() { + return world == null ? null : world.getTileEntity( + new BlockPos(masterBlock.x, masterBlock.y, masterBlock.z) + ); + } + + @Override + public void setMasterBlock(BlockPos pos) { + masterBlock = new HashedBlockPosition(pos); } + @Override + public void setComplete(BlockPos pos) { + } + + @Override + public void setIncomplete() { + masterBlock.y = -1; + } + + // --- Lifecycle / bus registration --- + + @Override + public void onLoad() { + if (world == null) return; + + if (!world.isRemote) { + // Only listen to rocket events if we actually have a rocket + if (linkedRocket != null && !registeredBus) { + MinecraftForge.EVENT_BUS.register(this); + registeredBus = true; + primeSnapshotsFromRocket(); // immediate stats/fuel refresh + } + + if (!initPower) { + boolean now = world.isBlockIndirectlyGettingPowered(pos) > 0; + isPoweredCached = now; + was_powered = now; + initPower = true; + } + + // Status staleness handling unchanged: + boolean stale = lastStatusTick == 0L + || (world.getTotalWorldTime() - lastStatusTick) > STATUS_STALE_TICKS; + + if (stale || (linkedRocket == null && mission == null)) { + clearUiStatus(); + lastStatusTick = 0L; + } else { + pushState(); + } + } + } + + + @Override public void invalidate() { super.invalidate(); + if (!world.isRemote && registeredBus) { + MinecraftForge.EVENT_BUS.unregister(this); + registeredBus = false; + } + + // Tell the assembler that this infra is gone + if (!world.isRemote && hasMaster()) { + TileEntity master = getMasterBlock(); + if (master instanceof TileRocketAssemblingMachine) { + ((TileRocketAssemblingMachine) master).removeConnectedInfrastructure(this); + } + } + if (linkedRocket != null) { linkedRocket.unlinkInfrastructure(this); unlinkRocket(); @@ -71,49 +268,311 @@ public void invalidate() { } } - public boolean getEquivalentPower() { - //if (state == RedstoneState.OFF) - // return false; - boolean state2 = world.isBlockIndirectlyGettingPowered(pos) > 0; + @Override + public void onChunkUnload() { + super.onChunkUnload(); + // This tile remains linked across unload/reload and during flight/space. + if (!world.isRemote && registeredBus) { + MinecraftForge.EVENT_BUS.unregister(this); + registeredBus = false; + } + } + - //if (state == RedstoneState.INVERTED) - // state2 = !state2; - return state2; + // --- Redstone power caching via block neighbor callbacks --- + + @Deprecated + public boolean getEquivalentPower() { + return world.isBlockIndirectlyGettingPowered(pos) > 0; } @Override public void onAdjacentBlockUpdated() { + if (world == null || world.isRemote) return; + boolean now = world.isBlockIndirectlyGettingPowered(pos) > 0; + boolean rising = now && !isPoweredCached; + + // Update cache first so it stays correct even with no rocket linked + isPoweredCached = now; + was_powered = now; + + if (rising && linkedRocket != null) { + linkedRocket.prepareLaunch(); + markDirty(); + } } + + + // --- IInfrastructure --- + @Override public int getMaxLinkDistance() { return 300000; } + @Override + public boolean disconnectOnLiftOff() { + return false; + } + + @Override + public boolean linkRocket(EntityRocketBase rocket) { + if (rocket == null || world == null) { + return false; + } + + // If we are bound to an assembler, we only trust: + // - the rocket we already own, or + // - a rocket that the assembler just claimed for us. + if (!world.isRemote && hasMaster()) { + final int rocketId = rocket.getEntityId(); + + boolean allowed = false; + + // 1) Already our rocket? Always allow re-connect (teleports, dim changes). + if (this.linkedRocket != null && this.linkedRocket == rocket) { + allowed = true; + } else { + // 2) Else, require a fresh assembler claim. + boolean haveClaim = + (expectedRocketId == rocketId) && + (world.getTotalWorldTime() <= expectedRocketExpiry); + + if (haveClaim) { + allowed = true; + } + } + + if (!allowed) { + // This rocket has us in its infra list, but assembler did NOT bless it. + // Clean its list and refuse. + rocket.unlinkInfrastructure(this); + return false; + } + if (!isRocketAllowedForMaster(rocket)) { + rocket.unlinkInfrastructure(this); + return false; + } + } + + // From here: either we have no master (free-floating infra), + // or the assembler/owner check passed. + this.linkedRocket = rocket; + + // Always listen to events on the server + if (!world.isRemote && !registeredBus) { + MinecraftForge.EVENT_BUS.register(this); + registeredBus = true; + } + + if (!world.isRemote) { + final int dim = rocket.world.provider.getDimension(); + final int eid = rocket.getEntityId(); + // final double rx = rocket.posX, ry = rocket.posY, rz = rocket.posZ; + + final zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType ft = + rocket.getRocketFuelType(); + final int fAmt = (ft != null) ? rocket.getFuelAmount(ft) : 0; + final int fCap = (ft != null) ? rocket.getFuelCapacity(ft) : 0; + + int thrust = -1, weight = -1; + if (rocket instanceof zmaster587.advancedRocketry.entity.EntityRocket) { + try { + zmaster587.advancedRocketry.entity.EntityRocket er = + (zmaster587.advancedRocketry.entity.EntityRocket) rocket; + zmaster587.advancedRocketry.api.StatsRocket stats = er.getRocketStats(); + if (stats != null) { + thrust = (int) stats.getThrust(); + weight = (int) stats.getWeight(); + } + } catch (Throwable t) { /* keep simple */ } + } + + // Fresh snapshot + UI as before + primeSnapshotsFromRocket(); + + boolean returning = (rocket instanceof EntityRocket) + && ((EntityRocket) rocket).isInOrbit() + && ((EntityRocket) rocket).isInFlight(); + + if (returning) { + uiStatus = 4; // deorbiting + lastStatusTick = world.getTotalWorldTime(); + pushState(); + } else { + clearUiStatus(); + lastStatusTick = 0L; + } + } + return true; + } + + + + @Override + public void unlinkRocket() { + linkedRocket = null; + + // reset snapshots + snapHeight = snapVel = 0; + snapFuel = snapFuelCap = 0; + snapOx = snapOxCap = 0; + + if (!world.isRemote) { + lastComparator = 0; + world.updateComparatorOutputLevel(pos, world.getBlockState(pos).getBlock()); + + // Keep "Reached orbit" visible while the mission is active. + if (mission == null) { + clearUiStatus(); + lastStatusTick = 0L; // reset tick + } + } + } + + + // --- Ticking --- + @Override public void update() { + if (world.isRemote) return; + + // One-time prime (in case no neighbor event has fired yet) + if (!initPower) { + isPoweredCached = world.isBlockIndirectlyGettingPowered(pos) > 0; + initPower = true; + } + if (!world.isRemote) { - if (linkedRocket instanceof EntityRocket) { - if ((int) (15 * ((EntityRocket) linkedRocket).getRelativeHeightFraction()) != (int) (15 * ((EntityRocket) linkedRocket).getPreviousRelativeHeightFraction())) { - markDirty(); - } - if (getEquivalentPower() && linkedRocket != null) { - if (!was_powered) { - System.out.println("prepare launch (redstone powered)"); - linkedRocket.prepareLaunch(); - //System.out.println("launching..."); - was_powered = true; + long age = world.getTotalWorldTime() - lastStatusTick; + + // Aborted + if (uiStatus == 6 && age > STATUS_STALE_TICKS) clearUiStatus(); + + // Reached orbit — only time out when no mission is linked + if (uiStatus == 3 && mission == null && age > STATUS_STALE_TICKS) clearUiStatus(); + + // Landed + if (uiStatus == 5 && age > STATUS_STALE_TICKS) clearUiStatus(); + } + // Runs infrequently to recover from any missed neighbor events. + if (world.getTotalWorldTime() % 100 == 0) { // every 100 ticks + boolean polled = world.isBlockIndirectlyGettingPowered(pos) > 0; + isPoweredCached = polled; // DO NOT trigger launch here; just reconcile the cache + } + // Idle fast-exit + if (linkedRocket == null) { return; } + + if (snapFuelCap == 0 && linkedRocket.getRocketFuelType() != null) { + primeSnapshotsFromRocket(); + } + // ---- height + velocity snapshots, every T_HEIGHTVEL_TICKS ---- + if (++heightVelTick >= Math.max(1, T_HEIGHTVEL_TICKS)) { + heightVelTick = 0; + + snapHeight = (int) linkedRocket.posY; + snapVel = (int) (linkedRocket.motionY * 100); + + // comparator (0–15) change-only, every T_COMPARATOR_TICKS + if (++comparatorTick >= Math.max(1, T_COMPARATOR_TICKS)) { + comparatorTick = 0; + if (linkedRocket instanceof EntityRocket) { + int comp = (int)(15 * ((EntityRocket) linkedRocket).getRelativeHeightFraction()); + if (comp != lastComparator) { + lastComparator = comp; + world.updateComparatorOutputLevel(pos, world.getBlockState(pos).getBlock()); } } } - if(!getEquivalentPower()){ - was_powered = false; // - } + } + + // ---- fuel snapshots, every T_FUEL_TICKS ---- + if (++fuelTick >= Math.max(1, T_FUEL_TICKS)) { + fuelTick = 0; + + // Original semantics: + // - id=2 shows the *active* rocket fuel + // - id=6 shows oxidizer independently + final FuelRegistry.FuelType active = linkedRocket.getRocketFuelType(); + snapFuel = (active != null) ? linkedRocket.getFuelAmount(active) : 0; + snapFuelCap = (active != null) ? linkedRocket.getFuelCapacity(active) : 0; + + snapOx = linkedRocket.getFuelAmount(FuelRegistry.FuelType.LIQUID_OXIDIZER); + snapOxCap = linkedRocket.getFuelCapacity(FuelRegistry.FuelType.LIQUID_OXIDIZER); + + refreshCapsFromRocket(); + } + } + + // --- Forge Rocket Events -> authorititative UI status (server -> client via TE update) --- + + @SubscribeEvent(priority = EventPriority.LOWEST) + public void onPreLaunch(RocketEvent.RocketPreLaunchEvent e) { + if (world == null || world.isRemote) return; + if (linkedRocket != null && e.getEntity() == linkedRocket) { + uiStatus = e.isCanceled() ? 6 : 1; + if (!e.isCanceled()) lastAbortReason = ""; // fresh launch, drop old reason + lastStatusTick = world.getTotalWorldTime(); + pushState(); } } + @SubscribeEvent + public void onLaunch(RocketEvent.RocketLaunchEvent e) { + if (world == null || world.isRemote) return; + if (linkedRocket != null && e.getEntity() == linkedRocket) { + uiStatus = 2; + lastStatusTick = world.getTotalWorldTime(); + pushState(); + } + } + + @SubscribeEvent + public void onOrbit(RocketEvent.RocketReachesOrbitEvent e) { + if (world == null || world.isRemote) return; + if (linkedRocket != null && e.getEntity() == linkedRocket) { + uiStatus = 3; + lastStatusTick = world.getTotalWorldTime(); + pushState(); + } + } + + @SubscribeEvent + public void onDeorbit(RocketEvent.RocketDeOrbitingEvent e) { + if (world == null || world.isRemote) return; + if (linkedRocket != null && e.getEntity() == linkedRocket) { + uiStatus = 4; // reuse “landed”/returning state + lastStatusTick = world.getTotalWorldTime(); + pushState(); + } + } + + @SubscribeEvent + public void onLanded(RocketEvent.RocketLandedEvent e) { + if (world == null || world.isRemote) return; + if (linkedRocket != null && e.getEntity() == linkedRocket) { + uiStatus = 5; + lastStatusTick = world.getTotalWorldTime(); + pushState(); + } + } + + @SubscribeEvent + public void onAbort(RocketEvent.RocketAbortEvent e) { + if (world == null || world.isRemote) return; + if (linkedRocket != null && e.getEntity() == linkedRocket) { + uiStatus = 6; // “aborted” + lastAbortReason = (e.reason == null) ? "" : e.reason; + lastStatusTick = world.getTotalWorldTime(); + pushState(); + } + } + + + // --- Linker flow --- @Override public boolean onLinkStart(@Nonnull ItemStack item, TileEntity entity, EntityPlayer player, World world) { @@ -128,7 +587,10 @@ public boolean onLinkStart(@Nonnull ItemStack item, TileEntity entity, EntityPla } if (player.world.isRemote) - Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage(new TextComponentTranslation("%s %s", new TextComponentTranslation("msg.monitoringStation.link"), ": " + getPos().getX() + " " + getPos().getY() + " " + getPos().getZ())); + Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage( + new TextComponentTranslation("%s %s", + new TextComponentTranslation("msg.monitoringStation.link"), + ": " + getPos().getX() + " " + getPos().getY() + " " + getPos().getZ())); return true; } @@ -139,148 +601,349 @@ public boolean onLinkComplete(@Nonnull ItemStack item, TileEntity entity, Entity return false; } + // --- NBT / TE sync --- + @Override - public void unlinkRocket() { - linkedRocket = null; + public NBTTagCompound getUpdateTag() { + return writeToNBT(new NBTTagCompound()); } @Override - public boolean disconnectOnLiftOff() { - return false; + public SPacketUpdateTileEntity getUpdatePacket() { + NBTTagCompound tag = new NBTTagCompound(); + writeToNBT(tag); + return new SPacketUpdateTileEntity(pos, 0, tag); } @Override - public boolean linkRocket(EntityRocketBase rocket) { - this.linkedRocket = rocket; - return true; + public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { + readFromNBT(pkt.getNbtCompound()); } - @Override - public NBTTagCompound getUpdateTag() { - return writeToNBT(new NBTTagCompound()); + private void refreshCapsFromRocket() { + if (linkedRocket == null) return; + final zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType active = linkedRocket.getRocketFuelType(); + snapFuelCap = (active != null) ? linkedRocket.getFuelCapacity(active) : 0; + snapOxCap = linkedRocket.getFuelCapacity(FuelRegistry.FuelType.LIQUID_OXIDIZER); + + // persist fallbacks so a restart still has sane totals + if (snapFuelCap > 0) lastKnownFuelCap = snapFuelCap; + if (snapOxCap > 0) lastKnownOxCap = snapOxCap; } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); - - //state = RedstoneState.values()[nbt.getByte("redstoneState")]; - //redstoneControl.setRedstoneState(state); was_powered = nbt.getBoolean("was_powered"); if (nbt.hasKey("missionID")) { long id = nbt.getLong("missionID"); - int dimid = nbt.getInteger("missionDimId"); - SatelliteBase sat = DimensionManager.getInstance().getSatellite(id); - - if (sat instanceof IMission) + if (sat instanceof IMission) { mission = (IMission) sat; + } + } + uiStatus = nbt.getInteger("uiStatus"); + lastStatusTick = nbt.getLong("lastStatusTick"); + lastAbortReason = nbt.hasKey("abortReason") ? nbt.getString("abortReason") : ""; + lastKnownFuelCap = nbt.getInteger("lastFuelCap"); + lastKnownOxCap = nbt.getInteger("lastOxCap"); + + if (nbt.hasKey("masterY") && nbt.getInteger("masterY") > -1) { + int mx = nbt.getInteger("masterX"); + int my = nbt.getInteger("masterY"); + int mz = nbt.getInteger("masterZ"); + masterBlock = new HashedBlockPosition(mx, my, mz); + } + + // client: force GUI labels to refresh + if (world != null && world.isRemote) { + lastUiStatusShown = -1; } } + @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); - //nbt.setByte("redstoneState", (byte) state.ordinal()); nbt.setBoolean("was_powered", was_powered); if (mission != null) { nbt.setLong("missionID", mission.getMissionId()); nbt.setInteger("missionDimId", mission.getOriginatingDimension()); } + nbt.setInteger("uiStatus", uiStatus); + nbt.setLong("lastStatusTick", lastStatusTick); + nbt.setString("abortReason", lastAbortReason == null ? "" : lastAbortReason); + nbt.setInteger("lastFuelCap", lastKnownFuelCap); + nbt.setInteger("lastOxCap", lastKnownOxCap); + + if (hasMaster()) { + nbt.setInteger("masterX", masterBlock.x); + nbt.setInteger("masterY", masterBlock.y); + nbt.setInteger("masterZ", masterBlock.z); + } return nbt; } + + // --- LibVulpes network bridge --- + @Override public void writeDataToNetwork(ByteBuf out, byte id) { - if (id == 1) - out.writeLong(mission == null ? -1 : mission.getMissionId()); - //else if (id == 2) - //out.writeByte(state.ordinal()); + if (id == 1) out.writeLong(mission == null ? -1 : mission.getMissionId()); + else if (id == TAB_SWITCH) out.writeShort(tabModule.getTab()); } @Override - public void readDataFromNetwork(ByteBuf in, byte packetId, - NBTTagCompound nbt) { - if (packetId == 1) { - nbt.setLong("id", in.readLong()); - } else if (packetId == 2) { - nbt.setByte("state", in.readByte()); - } + public void readDataFromNetwork(ByteBuf in, byte packetId, NBTTagCompound nbt) { + if (packetId == 1) nbt.setLong("id", in.readLong()); + else if (packetId == 2) nbt.setByte("state", in.readByte()); + else if (packetId == TAB_SWITCH) nbt.setShort("tab", in.readShort()); } @Override - public void useNetworkData(EntityPlayer player, Side side, byte id, - NBTTagCompound nbt) { + public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompound nbt) { if (id == 1) { long idNum = nbt.getLong("id"); if (idNum == -1) { mission = null; - setMissionText(); + if (world.isRemote && missionText != null) setMissionText(); } else { SatelliteBase base = DimensionManager.getInstance().getSatellite(idNum); - if (base instanceof IMission) { mission = (IMission) base; - setMissionText(); + if (world.isRemote && missionText != null) setMissionText(); } } - } else if (id == 2) { - //state = RedstoneState.values()[nbt.getByte("state")]; - //redstoneControl.setRedstoneState(state); } + else if (id == 2) { + // redstone control path was commented in original; preserved + } + else if (id == TAB_SWITCH && !world.isRemote) { + tabModule.setTab(nbt.getShort("tab")); + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); + } if (id == 100) { - if (linkedRocket != null) + if (linkedRocket != null) { + // always re-prime before launch to avoid stale weight/fuel decisions + if (linkedRocket instanceof EntityRocket) { + ((EntityRocket) linkedRocket).recalculateStats(); + } + refreshCapsFromRocket(); + primeSnapshotsFromRocket(); // reflect any last-second loading linkedRocket.prepareLaunch(); + } else { + if (!world.isRemote) { + player.sendMessage(new TextComponentTranslation("msg.monitoringStation.noLinkedRocket")); + } + } } } + + // --- GUI / Modules --- @Override public List getModules(int ID, EntityPlayer player) { - LinkedList modules = new LinkedList<>(); - modules.add(new ModuleButton(20, 40, 0, "Launch!", this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); - modules.add(new ModuleProgress(98, 4, 0, new IndicatorBarImage(2, 7, 12, 81, 17, 0, 6, 6, 1, 0, EnumFacing.UP, TextureResources.rocketHud), this)); - modules.add(new ModuleProgress(120, 14, 1, new IndicatorBarImage(2, 95, 12, 71, 17, 0, 6, 6, 1, 0, EnumFacing.UP, TextureResources.rocketHud), this)); - modules.add(new ModuleProgress(142, 14, 2, new ProgressBarImage(2, 173, 12, 71, 17, 6, 3, 69, 1, 1, EnumFacing.UP, TextureResources.rocketHud), this)); - modules.add(new ModuleProgress(148, 14, 6, new ProgressBarImage(2, 173, 12, 71, 17, 75, 3, 69, 1, 1, EnumFacing.UP, TextureResources.rocketHud), this)); + // Tabs control + modules.add(tabModule); - //modules.add(redstoneControl); - setMissionText(); + if (tabModule.getTab() == 0) { + // === STATUS TAB === + modules.add(new ModuleButton(20, 40, 0, LibVulpes.proxy.getLocalizedString("msg.monitoringStation.buttonLaunch"), this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); - modules.add(missionText); - modules.add(new ModuleProgress(30, 110, 3, TextureResources.progressToMission, this)); - modules.add(new ModuleProgress(30, 120, 4, TextureResources.workMission, this)); - modules.add(new ModuleProgress(30, 130, 5, TextureResources.progressFromMission, this)); + if (world.isRemote) { + launchStatus = new ModuleText(88, 92, "", 0xFFFFFF22, true); // centered + modules.add(launchStatus); - if (!world.isRemote) { - PacketHandler.sendToPlayer(new PacketMachine(this, (byte) 1), player); + abortDetail = new ModuleText(88, 108, "", 0xFF4444, true); // centered + modules.add(abortDetail); + + lastUiStatusShown = -1; + } + + modules.add(new ModuleProgress(98, 4, 0, new IndicatorBarImage(2, 7, 12, 81, 17, 0, 6, 6, 1, 0, EnumFacing.UP, TextureResources.rocketHud), this)); + modules.add(new ModuleProgress(120, 14, 1, new IndicatorBarImage(2, 95, 12, 71, 17, 0, 6, 6, 1, 0, EnumFacing.UP, TextureResources.rocketHud), this)); + modules.add(new ModuleProgress(142, 14, 2, new ProgressBarImage(2,173, 12, 71, 17, 6, 3, 69, 1, 1, EnumFacing.UP, TextureResources.rocketHud), this)); + modules.add(new ModuleProgress(148, 14, 6, new ProgressBarImage(2,173, 12, 71, 17,75, 3, 69, 1, 1, EnumFacing.UP, TextureResources.rocketHud), this)); + + if (!world.isRemote) { + PacketHandler.sendToPlayer(new PacketMachine(this, (byte)1), player); + pushState(); + } + return modules; } - return modules; - } + // === MISSION TAB === + { + final boolean hasMission = mission != null; + + // If there is NO mission: show a single centered line and exit early + if (!hasMission) { + modules.add(new ModuleText( + 88, 72, + LibVulpes.proxy.getLocalizedString("msg.monitoringStation.missionNoActiveMission"), + 0x2b2b2b, // color + true // centered + )); + if (!world.isRemote) { + PacketHandler.sendToPlayer(new PacketMachine(this, (byte)1), player); + pushState(); + } + return modules; + } + + // ---- Has mission: structured list ---- + final String typeLine; + { + String cls = mission.getClass().getSimpleName().toLowerCase(); + typeLine = cls.contains("gas") + ? LibVulpes.proxy.getLocalizedString("msg.monitoringStation.mission.type.gas") // "Gas Collection Mission" + : LibVulpes.proxy.getLocalizedString("msg.monitoringStation.mission.type.ore"); // "Asteroid Mining Mission" + } + + + modules.add(new ModuleText( + 88, 16, net.minecraft.util.text.TextFormatting.BOLD + typeLine + net.minecraft.util.text.TextFormatting.RESET, + 0x2b2b2b, + true // centered + )); + + // Decide mission type once (use the text you already built) + final boolean isGas = typeLine.toLowerCase().contains("gas"); + + // Target: if GAS mission, show the chosen fluid; otherwise keep default + if (isGas) { + String gasLabel = ""; + try { + if (mission instanceof zmaster587.advancedRocketry.mission.MissionGasCollection) { + net.minecraftforge.fluids.Fluid f = + ((zmaster587.advancedRocketry.mission.MissionGasCollection) mission).getGasFluid(); + if (f != null) { + gasLabel = new net.minecraftforge.fluids.FluidStack(f, 1).getLocalizedName(); + } + } + } catch (Throwable t) { /* be defensive */ } + + modules.add(new ModuleText( + 10, 39, + (gasLabel.isEmpty() + ? LibVulpes.proxy.getLocalizedString("msg.monitoringStation.mission.target.default") + : LibVulpes.proxy.getLocalizedString("msg.monitoringStation.mission.targetPrefix") + " " + gasLabel), + 0x2b2b2b + )); + } else { + // ---------- NON-GAS (ORE) SECTION — single, minimal block ---------- + String oreType = ""; + String shortId = ""; + + try { + if (mission instanceof zmaster587.advancedRocketry.mission.MissionOreMining) { + zmaster587.advancedRocketry.mission.MissionOreMining m = + (zmaster587.advancedRocketry.mission.MissionOreMining) mission; + + oreType = m.getAsteroidTypeOrEmpty(); + Long aUuid = m.getAsteroidUUIDOrNull(); + + if (aUuid != null) { + long base = aUuid; + long th = Integer.toUnsignedLong((oreType == null ? "" : oreType).hashCode()); + long z = base ^ (th << 1); + z += 0x9E3779B97F4A7C15L; + z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L; + z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL; + z = (z ^ (z >>> 31)); + String hex = Long.toUnsignedString(z, 16).toUpperCase(); + shortId = (hex.length() > 6) ? hex.substring(hex.length() - 6) : hex; + } + } + } catch (Throwable t) { /* defensive */ } + + // Line 1: Asteroid: (or just "Asteroid:" if id missing) + String lineAsteroid = LibVulpes.proxy.getLocalizedString("msg.monitoringStation.mission.Asteroid.targetPrefix") + + (shortId.isEmpty() ? "" : " " + shortId); + modules.add(new ModuleText(10, 39, lineAsteroid, 0x2b2b2b)); + + // Line 2: Type: (omit if unknown) + if (oreType != null && !oreType.isEmpty()) { + String lineType = LibVulpes.proxy.getLocalizedString("msg.monitoringStation.mission.asteroidIdPrefix") + + " " + oreType; + modules.add(new ModuleText(10, 53, lineType, 0x2b2b2b)); + } + } + + + // --- Specific line per mission type --- + if (isGas) { + // Read planned harvest written by the rocket into the mission's persist NBT + long plannedMb = -1L; + try { + if (mission instanceof zmaster587.advancedRocketry.mission.MissionResourceCollection) { + plannedMb = ((zmaster587.advancedRocketry.mission.MissionResourceCollection) mission) + .getPlannedHarvestMbOrDefault(); + } + } catch (Throwable t) { /* be defensive */ } + + final String plannedText = (plannedMb >= 0) + ? (LibVulpes.proxy.getLocalizedString("msg.monitoringStation.mission.plannedAmountPrefix") + " " + plannedMb + " mB") + : LibVulpes.proxy.getLocalizedString("msg.monitoringStation.mission.plannedAmountPending"); + + modules.add(new ModuleText(10, 53, plannedText, 0x2b2b2b)); + } + //else { if we want to add ore-specific lines later, show loot etc. } + + // Duration text (above the stage bars, like original) + missionText = new ModuleText(88, 94, "", 0x2b2b2b, true); + setMissionText(); + modules.add(missionText); + // Stage bars just above the time block + modules.add(new ModuleProgress(30, 110, 3, TextureResources.progressToMission, this)); + modules.add(new ModuleProgress(30, 120, 4, TextureResources.workMission, this)); + modules.add(new ModuleProgress(30, 130, 5, TextureResources.progressFromMission, this)); + + if (!world.isRemote) { + PacketHandler.sendToPlayer(new PacketMachine(this, (byte)1), player); + pushState(); + } + return modules; + } + } + private void setMissionText() { + // If the text widget isn’t built yet (e.g., GUI closed or on other tab), just bail out. + if (missionText == null) return; + if (mission != null) { int time = mission.getTimeRemainingInSeconds(); int seconds = time % 60; int minutes = (time / 60) % 60; int hours = time / 3600; - missionText.setText(((SatelliteBase) mission).getName() + LibVulpes.proxy.getLocalizedString("msg.monitoringStation.progress") + String.format("\n%02dhr:%02dm:%02ds", hours, minutes, seconds)); - } else + missionText.setText( + LibVulpes.proxy.getLocalizedString("msg.monitoringStation.progress") + + String.format(" %02d:%02d:%02d", hours, minutes, seconds) + ); + } else { missionText.setText(LibVulpes.proxy.getLocalizedString("msg.monitoringStation.missionProgressNA")); + } } @Override public void onInventoryButtonPressed(int buttonId) { if (buttonId != -1) - PacketHandler.sendToServer(new PacketMachine(this, (byte) (buttonId + 100))); - else { - //state = redstoneControl.getState(); - PacketHandler.sendToServer(new PacketMachine(this, (byte) 2)); - } + PacketHandler.sendToServer(new PacketMachine(this, (byte)(buttonId + 100))); + else + PacketHandler.sendToServer(new PacketMachine(this, (byte)2)); + } + + private static String wrapToWidthClient(String s, int maxWidthPx) { + if (s == null || s.isEmpty()) return ""; + net.minecraft.client.gui.FontRenderer fr = net.minecraft.client.Minecraft.getMinecraft().fontRenderer; + java.util.List lines = fr.listFormattedStringToWidth(s, Math.max(1, maxWidthPx)); + return String.join("\n", lines); } @Override @@ -288,31 +951,96 @@ public String getModularInventoryName() { return "container.monitoringstation"; } + @SideOnly(Side.CLIENT) + private static String trAbortReason(String raw) { + if (raw == null || raw.isEmpty()) return ""; + + // Support "key|arg1|arg2" (easy to emit server-side) + final String delim = "|"; + String key = raw; + Object[] args = null; + + if (raw.indexOf(delim) >= 0) { + String[] parts = raw.split("\\|", -1); + if (parts.length > 0) { + key = parts[0]; + if (parts.length > 1) { + args = new Object[parts.length - 1]; + System.arraycopy(parts, 1, args, 0, args.length); + } + } + } + + String out; + if (args != null) { + out = net.minecraft.util.text.translation.I18n.translateToLocalFormatted(key, args); + } else { + out = net.minecraft.util.text.translation.I18n.translateToLocal(key); + } + + // If missing, vanilla returns the key unchanged — fall back to original raw + if (out == null || out.isEmpty() || out.equals(key)) { + return raw; + } + return out; + } + + @Override public float getNormallizedProgress(int id) { + if (world.isRemote) { + // Status tab label + if (launchStatus != null && uiStatus != lastUiStatusShown) { + lastUiStatusShown = uiStatus; + + String header = ""; + String detail = ""; + + switch (uiStatus) { + case 1: header = LibVulpes.proxy.getLocalizedString("msg.monitoringStation.prelaunch"); break; + case 2: header = LibVulpes.proxy.getLocalizedString("msg.monitoringStation.launching"); break; + case 3: header = LibVulpes.proxy.getLocalizedString("msg.monitoringStation.orbit"); break; + case 4: header = LibVulpes.proxy.getLocalizedString("msg.monitoringStation.deorbiting"); break; + case 5: header = LibVulpes.proxy.getLocalizedString("msg.monitoringStation.landed"); break; + case 6: + header = LibVulpes.proxy.getLocalizedString("msg.monitoringStation.aborted"); + if (lastAbortReason != null && !lastAbortReason.isEmpty()) { + detail = trAbortReason(lastAbortReason); + } + break; + default: + header = ""; + } + launchStatus.setText(header); + if (abortDetail != null) { + final int ABORT_WRAP_WIDTH = 150; + abortDetail.setText(wrapToWidthClient(detail, ABORT_WRAP_WIDTH)); + } + } + + // Mission tab duration label (make it live) + if (mission != null && missionText != null) { + setMissionText(); + } + } + if (id == 1) { return Math.max(Math.min(0.5f + (getProgress(id) / (float) getTotalProgress(id)), 1), 0f); } else if (id == 3) { - if (mission == null) - return 0f; + if (mission == null) return 0f; return (float) Math.min(3f * mission.getProgress(this.world), 1f); } else if (id == 4) { - if (mission == null) - return 0f; + if (mission == null) return 0f; return (float) Math.min(Math.max(3f * (mission.getProgress(this.world) - 0.333f), 0f), 1f); } else if (id == 5) { - if (mission == null) - return 0f; + if (mission == null) return 0f; return (float) Math.min(Math.max(3f * (mission.getProgress(this.world) - 0.666f), 0f), 1f); } - //keep text updated - if (world.isRemote && mission != null) - setMissionText(); - return Math.min(getProgress(id) / (float) getTotalProgress(id), 1.0f); } + @Override public void setProgress(int id, int progress) { if (id == 0) @@ -325,60 +1053,65 @@ else if (id == 6) oxidizerFuelLevel = progress; } + /** Pulls a full, fresh snapshot from the linked rocket and pushes a TE update. */ + private void primeSnapshotsFromRocket() { + if (world == null || world.isRemote) return; + if (linkedRocket == null) return; + + // 1) make sure the rocket’s internal stats are up to date + if (linkedRocket instanceof EntityRocket) { + ((EntityRocket) linkedRocket).recalculateStats(); // calls storage.recalculateStats(stats) + } + + // 2) fresh fuel types/capacities + final zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType active = linkedRocket.getRocketFuelType(); + snapFuel = (active != null) ? linkedRocket.getFuelAmount(active) : 0; + snapFuelCap = (active != null) ? linkedRocket.getFuelCapacity(active) : 0; + + snapOx = linkedRocket.getFuelAmount(zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType.LIQUID_OXIDIZER); + snapOxCap = linkedRocket.getFuelCapacity(zmaster587.advancedRocketry.api.fuel.FuelRegistry.FuelType.LIQUID_OXIDIZER); + + // keep persisted fallbacks up to date + if (snapFuelCap > 0) lastKnownFuelCap = snapFuelCap; + if (snapOxCap > 0) lastKnownOxCap = snapOxCap; + + // 3) height/velocity + snapHeight = (int) linkedRocket.posY; + snapVel = (int) (linkedRocket.motionY * 100); + + // 4) make comparator reflect fresh height right away + lastComparator = -1; + world.updateComparatorOutputLevel(pos, world.getBlockState(pos).getBlock()); + + // 5) tell clients now (no waiting for the periodic tick) + pushState(); + } + @Override public int getProgress(int id) { - //Try to keep client synced with server, this also allows us to put the monitor on a different world altogether - if (world.isRemote) - if (mission != null && id == 0) - return getTotalProgress(id); - else if (id == 0) - return rocketHeight; - else if (id == 1) - return velocity; - else if (id == 2) - return fuelLevel; - else if (id == 6) - return oxidizerFuelLevel; - - if (linkedRocket == null) + // Client: use client-side cached fields (preserve original mission/height quirk) + if (world.isRemote) { + if (mission != null && id == 0) return getTotalProgress(id); // original oddity preserved + if (id == 0) return rocketHeight; + if (id == 1) return velocity; + if (id == 2) return fuelLevel; + if (id == 6) return oxidizerFuelLevel; return 0; - - if (id == 0) - return (int) linkedRocket.posY; - else if (id == 1) - return (int) (linkedRocket.motionY * 100); - else if (id == 2) - return linkedRocket.getFuelAmount(linkedRocket.getRocketFuelType()); - else if (id == 6) - return linkedRocket.getFuelAmount(FuelRegistry.FuelType.LIQUID_OXIDIZER); - + } + // Server: return snapshots only (cheap) + if (id == 0) return snapHeight; + if (id == 1) return snapVel; + if (id == 2) return snapFuel; // active fuel amount + if (id == 6) return snapOx; // oxidizer amount return 0; } @Override public int getTotalProgress(int id) { - if (id == 0) - return ARConfiguration.getCurrentConfig().orbit; - else if (id == 1) - return 1000; - else if (id == 2) - if (world.isRemote) - return maxFuelLevel; - else if (linkedRocket == null) - return 0; - else - return linkedRocket.getFuelCapacity(linkedRocket.getRocketFuelType()); - - else if (id == 6) - if (world.isRemote) - return maxFuelLevel; - else if (linkedRocket == null) - return 0; - else - return linkedRocket.getFuelCapacity(FuelRegistry.FuelType.LIQUID_OXIDIZER); - - - + if (id == 0) return ARConfiguration.getCurrentConfig().orbit; + if (id == 1) return 1000; + if (id == 2) return (world.isRemote ? maxFuelLevel : (snapFuelCap > 0 ? snapFuelCap : lastKnownFuelCap)); + if (id == 6) return (world.isRemote ? maxFuelLevel : (snapOxCap > 0 ? snapOxCap : lastKnownOxCap)); return 1; } @@ -393,19 +1126,31 @@ public void setTotalProgress(int id, int progress) { public boolean canInteractWithContainer(EntityPlayer entity) { return true; } + + @Override + public void onModuleUpdated(ModuleBase module) { + PacketHandler.sendToServer(new PacketMachine(this, TAB_SWITCH)); + } @Override public boolean linkMission(IMission mission) { this.mission = mission; - PacketHandler.sendToNearby(new PacketMachine(this, (byte) 1), world.provider.getDimension(), getPos(), 16); + // If we don’t already have a status, show “in orbit” while mission runs. + if (!world.isRemote) { + // If we were at idle/prelaunch/launching, move to "reached orbit" now. + if (uiStatus < 3) { + uiStatus = 3; + lastStatusTick = world.getTotalWorldTime(); + pushState(); + } + } return true; } @Override public void unlinkMission() { mission = null; - setMissionText(); - PacketHandler.sendToNearby(new PacketMachine(this, (byte) 1), world.provider.getDimension(), getPos(), 16); + if (missionText != null) setMissionText(); } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketUnloader.java b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketUnloader.java index 9fe29982a..1a72c8959 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketUnloader.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/infrastructure/TileRocketUnloader.java @@ -1,21 +1,22 @@ package zmaster587.advancedRocketry.tile.infrastructure; -import net.minecraft.inventory.IInventory; + import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ITickable; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.ItemHandlerHelper; import zmaster587.advancedRocketry.api.IInfrastructure; import zmaster587.advancedRocketry.tile.TileGuidanceComputer; +import zmaster587.advancedRocketry.tile.hatch.TileSatelliteHatch; import zmaster587.libVulpes.inventory.modules.IButtonInventory; -import zmaster587.libVulpes.inventory.modules.ModuleRedstoneOutputButton; import zmaster587.libVulpes.util.INetworkMachine; import zmaster587.libVulpes.util.ZUtils.RedstoneState; import java.util.List; public class TileRocketUnloader extends TileRocketLoader implements IInfrastructure, ITickable, IButtonInventory, INetworkMachine { - ModuleRedstoneOutputButton redstoneControl; - RedstoneState state; + public TileRocketUnloader() { super(); @@ -41,52 +42,94 @@ public String getModularInventoryName() { @Override public void update() { + if (world.isRemote || rocket == null) + return; + + // Throttle: only try to move items every TRANSFER_INTERVAL_TICKS + if (transferCooldown > 0) { + transferCooldown--; + return; + } + + boolean isAllowedToOperate = (inputstate == RedstoneState.OFF || + isStateActive(inputstate, getStrongPowerForSides(world, getPos()))); + + IItemHandler ownHandler = getOwnItemHandler(); + if (ownHandler == null || ownHandler.getSlots() == 0) { + // No destination handler: consider rocket not empty (no "done unloading" signal) + setRedstoneState(false); + return; + } - //Move a stack of items - if (!world.isRemote && rocket != null) { - boolean isAllowedToOperate = (inputstate == RedstoneState.OFF || isStateActive(inputstate, getStrongPowerForSides(world, getPos()))); - - List tiles = rocket.storage.getInventoryTiles(); - boolean foundStack = false; - boolean rocketContainsNoItems = true; - out: - //Function returns if something can be moved - for (TileEntity tile : tiles) { - if (tile instanceof IInventory && !(tile instanceof TileGuidanceComputer)) { - IInventory inv = ((IInventory) tile); - for (int i = 0; i < inv.getSizeInventory(); i++) { - if (!inv.getStackInSlot(i).isEmpty()) { - rocketContainsNoItems = false; - //Loop though this inventory's slots and find a suitible one - for (int j = 0; j < getSizeInventory(); j++) { - if (getStackInSlot(j).isEmpty()) { - if (isAllowedToOperate) { - inventory.setInventorySlotContents(j, inv.getStackInSlot(i)); - inv.setInventorySlotContents(i, ItemStack.EMPTY); - } - break out; - } else if (!inv.getStackInSlot(i).isEmpty() && isItemValidForSlot(j, inv.getStackInSlot(i))) { - if (isAllowedToOperate) { - ItemStack stack2 = inv.decrStackSize(i, getStackInSlot(j).getMaxStackSize() - getStackInSlot(j).getCount()); - getStackInSlot(j).setCount(getStackInSlot(j).getCount() + stack2.getCount()); - } - if (inv.getStackInSlot(i).isEmpty()) - break out; - foundStack = true; - } - } - } - - if (foundStack) - break out; - } + List tiles = rocket.storage.getInventoryTiles(); + boolean rocketIsEmpty = true; + + outer: + for (TileEntity tile : tiles) { + if (tile instanceof TileGuidanceComputer || tile instanceof TileSatelliteHatch) + continue; + + IItemHandler rocketHandler = getItemHandler(tile); + if (rocketHandler == null || rocketHandler.getSlots() == 0) + continue; + + int rocketSlots = rocketHandler.getSlots(); + + for (int rocketSlot = 0; rocketSlot < rocketSlots; rocketSlot++) { + ItemStack rocketStack = rocketHandler.getStackInSlot(rocketSlot); + + if (!rocketStack.isEmpty()) { + rocketIsEmpty = false; } - } - //Update redstone state - setRedstoneState(rocketContainsNoItems); + if (rocketStack.isEmpty()) + continue; + // If we are not allowed to operate, we only care about rocketIsEmpty for redstone + if (!isAllowedToOperate) { + continue; + } + + // Limit per-operation transfer, but DO NOT assume anything about slot max size + int maxToMove = Math.min(MAX_TRANSFER_PER_OPERATION, rocketStack.getCount()); + if (maxToMove <= 0) + continue; + + // Simulate extraction from rocket + ItemStack simulatedExtract = rocketHandler.extractItem(rocketSlot, maxToMove, true); + if (simulatedExtract.isEmpty()) + continue; + + // Simulate insertion into our own inventory + ItemStack simulatedRemainder = ItemHandlerHelper.insertItem(ownHandler, simulatedExtract, true); + int accepted = simulatedExtract.getCount() - simulatedRemainder.getCount(); + if (accepted <= 0) + continue; + + // Actually extract exactly what will fit + ItemStack actuallyExtracted = rocketHandler.extractItem(rocketSlot, accepted, false); + if (actuallyExtracted.isEmpty()) + continue; + + // Actually insert into our inventory + ItemStack remainder = ItemHandlerHelper.insertItem(ownHandler, actuallyExtracted, false); + + // Last-resort fallback for misbehaving mods: try to put remainder back + if (!remainder.isEmpty()) { + ItemHandlerHelper.insertItem(rocketHandler, remainder, false); + // Same note: if that still leaves items, they're from a broken handler. + } + + transferCooldown = TRANSFER_INTERVAL_TICKS; + markDirty(); + tile.markDirty(); + break outer; // only one transfer per operation + } } + + // Redstone: ON when rocketIsEmpty (unloading done) + setRedstoneState(rocketIsEmpty); } -} + +} diff --git a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileAreaGravityController.java b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileAreaGravityController.java index ce0354852..6b27cee81 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileAreaGravityController.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileAreaGravityController.java @@ -14,10 +14,12 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.relauncher.Side; import zmaster587.advancedRocketry.AdvancedRocketry; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; import zmaster587.advancedRocketry.inventory.TextureResources; +import zmaster587.advancedRocketry.inventory.modules.ModuleSideSelectorTooltipOverlay; import zmaster587.advancedRocketry.util.AudioRegistry; import zmaster587.advancedRocketry.util.GravityHandler; import zmaster587.libVulpes.LibVulpes; @@ -41,6 +43,7 @@ public class TileAreaGravityController extends TileMultiPowerConsumer implements {LibVulpesBlocks.blockAdvStructureBlock, 'P', LibVulpesBlocks.blockAdvStructureBlock}, {null, LibVulpesBlocks.blockAdvStructureBlock, null}} }; + int gravity; int progress; int radius; @@ -49,13 +52,20 @@ public class TileAreaGravityController extends TileMultiPowerConsumer implements private ModuleRedstoneOutputButton redstoneControl; private RedstoneState state; private ModuleText targetGrav, textRadius; + private String[] sideStateNames; private ModuleBlockSideSelector sideSelectorModule; public TileAreaGravityController() { - //numGravPylons = new ModuleText(10, 25, "Number Of Thrusters: ", 0xaa2020); textRadius = new ModuleText(6, 82, LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.radius") + "5", 0x202020); targetGrav = new ModuleText(6, 110, LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.targetgrav"), 0x202020); - sideSelectorModule = new ModuleBlockSideSelector(90, 15, this, LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.none"), LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.activeset"), LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.activeadd")); + + sideStateNames = new String[] { + LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.none"), + LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.activeset"), + LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.activeadd") + }; + + sideSelectorModule = new ModuleBlockSideSelector(90, 15, this, sideStateNames); redstoneControl = new ModuleRedstoneOutputButton(174, 4, 1, "", this); state = RedstoneState.OFF; @@ -75,24 +85,32 @@ public Object[][][] getStructure() { @Override public List getModules(int id, EntityPlayer player) { - List modules = new LinkedList<>();//super.getModules(id, player); - modules.add(toggleSwitch = new ModuleToggleSwitch(160, 5, 0, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonToggleImage, 11, 26, getMachineEnabled())); + List modules = new LinkedList<>(); + modules.add(toggleSwitch = new ModuleToggleSwitch(160, 5, 0, "", this, + zmaster587.libVulpes.inventory.TextureResources.buttonToggleImage, 11, 26, getMachineEnabled())); modules.add(new ModulePower(18, 20, getBatteries())); modules.add(sideSelectorModule); - modules.add(redstoneControl); - modules.add(new ModuleSlider(6, 120, 0, TextureResources.doubleWarningSideBarIndicator, this)); modules.add(new ModuleSlider(6, 90, 1, TextureResources.doubleWarningSideBarIndicator, this)); - modules.add(new ModuleText(42, 20, LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.targetdir.1") + "\n" + LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.targetdir.2"), 0x202020)); + modules.add(new ModuleText(42, 20, + LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.targetdir.1") + "\n" + + LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.targetdir.2"), + 0x202020)); modules.add(targetGrav); modules.add(textRadius); + + if (FMLCommonHandler.instance().getSide().isClient()) { + modules.add(new ModuleSideSelectorTooltipOverlay(90, 15, sideSelectorModule, sideStateNames)); + } + updateText(); return modules; } + public int getRadius() { return radius + 10; } @@ -119,11 +137,10 @@ public void setGravityMultiplier(double multiplier) { } private void updateText() { - if (world.isRemote) { - textRadius.setText(String.format("%s%d", LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.radius"), getRadius())); + if (world == null || !world.isRemote) return; + textRadius.setText(String.format("%s%d", LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.radius"), getRadius())); - targetGrav.setText(String.format("%s %.2f/%.2f", LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.targetgrav"), currentProgress, gravity / 100f)); - } + targetGrav.setText(String.format("%s %.2f/%.2f", LibVulpes.proxy.getLocalizedString("msg.gravitycontroller.targetgrav"), currentProgress, gravity / 100f)); } @Override diff --git a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileAstrobodyDataProcessor.java b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileAstrobodyDataProcessor.java index f80127fd1..8a7883e3b 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileAstrobodyDataProcessor.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileAstrobodyDataProcessor.java @@ -411,7 +411,8 @@ public List getModules(int ID, EntityPlayer player) { int xStart = 150; int yStart = 14; - modules.add(new ModuleText(15, 76, "Research", 0x404040)); + modules.add(new ModuleText(15, 76, LibVulpes.proxy.getLocalizedString("msg.abdp.research"), 0x404040)); + modules.add(new ModuleToggleSwitch(15, 86, 4, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonToggleImage, LibVulpes.proxy.getLocalizedString("msg.abdp.compositionresearch"), 11, 26, researchingAtmosphere)); modules.add(new ModuleToggleSwitch(65, 86, 5, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonToggleImage, LibVulpes.proxy.getLocalizedString("msg.abdp.distanceresearch"), 11, 26, researchingDistance)); diff --git a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileObservatory.java b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileObservatory.java index 576661819..65f5a9bd5 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileObservatory.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileObservatory.java @@ -13,6 +13,7 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraft.world.WorldServer; import net.minecraftforge.fml.relauncher.Side; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; @@ -20,6 +21,8 @@ import zmaster587.advancedRocketry.api.DataStorage.DataType; import zmaster587.advancedRocketry.inventory.TextureResources; import zmaster587.advancedRocketry.inventory.modules.ModuleData; +import zmaster587.advancedRocketry.inventory.modules.ModuleItemSlotButton; +import zmaster587.advancedRocketry.item.IDataItem; import zmaster587.advancedRocketry.item.ItemAsteroidChip; import zmaster587.advancedRocketry.item.ItemData; import zmaster587.advancedRocketry.tile.hatch.TileDataBus; @@ -37,22 +40,28 @@ import zmaster587.libVulpes.network.PacketMachine; import zmaster587.libVulpes.tile.multiblock.TileMultiBlock; import zmaster587.libVulpes.tile.multiblock.TileMultiPowerConsumer; +import zmaster587.libVulpes.tile.multiblock.TilePlaceholder; import zmaster587.libVulpes.util.EmbeddedInventory; import javax.annotation.Nonnull; import javax.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Random; +import java.util.Map; public class TileObservatory extends TileMultiPowerConsumer implements IModularInventory, IDataInventory, IGuiCallback { + private final java.util.Map savedDataBusNbt = new java.util.HashMap<>(); final static int openTime = 100; final static int observationTime = 1000; private static final Block[] lens = {AdvancedRocketryBlocks.blockLens, Blocks.GLASS}; private static final Object[][][] structure = new Object[][][]{ - + {{null, null, null, null, null}, {null, LibVulpesBlocks.blockStructureBlock, lens, LibVulpesBlocks.blockStructureBlock, null}, {null, LibVulpesBlocks.blockStructureBlock, LibVulpesBlocks.blockStructureBlock, LibVulpesBlocks.blockStructureBlock, null}, @@ -87,7 +96,14 @@ public class TileObservatory extends TileMultiPowerConsumer implements IModularI private static final short LIST_OFFSET = 100; private static final byte PROCESS_CHIP = 12; private static final byte SEED_CHANGE = 13; - private final int dataConsumedPerRefresh = 100; + private static final byte SYNC_PRINTED = 14; + private static final byte REQUEST_REOPEN = 15; + private static final byte SYNC_SEED = 16; + private final int dataConsumedPerRefresh = 100; // Distance data consumed per scan + private boolean pendingReopenAfterSeedSync = false; + // Dont allow duplicate chipwrites for the same seed + button + private java.util.HashSet printedButtonsThisSeed = new java.util.HashSet<>(); + private long printedSetSeed = -1; // track which seed the set belongs to int openProgress; EmbeddedInventory inv = new EmbeddedInventory(5); private int viewDistance; @@ -113,23 +129,107 @@ public float getOpenProgress() { return openProgress / (float) openTime; } + private void snapshotDataBusesBeforeTeardown() { + savedDataBusNbt.clear(); + + final Object[][][] struct = getStructure(); + if (struct == null || world == null) return; + + final zmaster587.libVulpes.util.Vector3F off = getControllerOffset(struct); + final EnumFacing front = getFrontDirection(world.getBlockState(pos)); + + for (int y = 0; y < struct.length; y++) { + for (int z = 0; z < struct[0].length; z++) { + for (int x = 0; x < struct[0][0].length; x++) { + if (struct[y][z][x] == null) continue; + + int gx = pos.getX() + (x - off.x) * front.getFrontOffsetZ() + - (z - off.z) * front.getFrontOffsetX(); + int gy = pos.getY() - y + off.y; + int gz = pos.getZ() - (x - off.x) * front.getFrontOffsetX() + - (z - off.z) * front.getFrontOffsetZ(); + BlockPos bp = new BlockPos(gx, gy, gz); + + TileEntity te = world.getTileEntity(bp); + if (te instanceof zmaster587.libVulpes.tile.multiblock.TilePlaceholder) { + te = ((zmaster587.libVulpes.tile.multiblock.TilePlaceholder) te).getReplacedTileEntity(); + } + + if (te instanceof zmaster587.advancedRocketry.tile.hatch.TileDataBus) { + NBTTagCompound tag = new NBTTagCompound(); + te.writeToNBT(tag); + savedDataBusNbt.put(bp.toLong(), tag); + } + } + } + } + } + + private void restoreDataBusesAfterTeardown() { + if (world == null || savedDataBusNbt.isEmpty()) return; + + try { + for (Map.Entry e : savedDataBusNbt.entrySet()) { + BlockPos bp = BlockPos.fromLong(e.getKey()); + TileEntity te = world.getTileEntity(bp); + + if (te instanceof TilePlaceholder) { + te = ((TilePlaceholder) te).getReplacedTileEntity(); + } + + if (te instanceof TileDataBus) { + TileDataBus bus = (TileDataBus) te; + bus.readFromNBT(e.getValue()); + bus.lockData(null); + bus.markDirty(); + world.notifyBlockUpdate(bp, world.getBlockState(bp), world.getBlockState(bp), 3); + } + } + } finally { + savedDataBusNbt.clear(); + } + } + + @Override protected void integrateTile(TileEntity tile) { super.integrateTile(tile); if (tile instanceof TileDataBus) { - dataCables.add((TileDataBus) tile); - ((TileDataBus) tile).lockData(((TileDataBus) tile).getDataObject().getDataType()); + TileDataBus bus = (TileDataBus) tile; + dataCables.add(bus); + + DataType type = bus.getDataObject().getDataType(); + + // If bus already has a meaningful type, preserve it. + if (type != null && type != DataType.UNDEFINED) { + bus.lockData(type); + } else { + // Default untyped buses to DISTANCE + bus.lockData(DataType.DISTANCE); + } } } + @Override - public void deconstructMultiBlock(World world, BlockPos destroyedPos, - boolean blockBroken, IBlockState state) { - super.deconstructMultiBlock(world, destroyedPos, blockBroken, state); + public void deconstructMultiBlock(World worldIn, BlockPos destroyedPos, + boolean blockBroken, IBlockState state) { + + if (!worldIn.isRemote) { + snapshotDataBusesBeforeTeardown(); + } + + super.deconstructMultiBlock(worldIn, destroyedPos, blockBroken, state); + + if (!worldIn.isRemote) { + restoreDataBusesAfterTeardown(); + } + viewDistance = 0; } + @Override protected void replaceStandardBlock(BlockPos newPos, IBlockState state, TileEntity tile) { @@ -233,35 +333,59 @@ protected void writeNetworkData(NBTTagCompound nbt) { nbt.setInteger("lastButton", lastButton); if (lastType != null && !lastType.isEmpty()) nbt.setString("lastType", lastType); + + nbt.setLong("printedSetSeed", printedSetSeed); + if (!printedButtonsThisSeed.isEmpty()) { + int[] arr = printedButtonsThisSeed.stream().mapToInt(Integer::intValue).toArray(); + nbt.setIntArray("printedButtons", arr); + } } @Override protected void readNetworkData(NBTTagCompound nbt) { + long prevSeed = this.lastSeed; super.readNetworkData(nbt); openProgress = nbt.getInteger("openProgress"); - isOpen = nbt.getBoolean("isOpen"); - viewDistance = nbt.getInteger("viewableDist"); lastSeed = nbt.getLong("lastSeed"); lastButton = nbt.getInteger("lastButton"); lastType = nbt.getString("lastType"); + + printedSetSeed = nbt.getLong("printedSetSeed"); + printedButtonsThisSeed.clear(); + int[] arr = nbt.getIntArray("printedButtons"); + if (arr != null) for (int v : arr) printedButtonsThisSeed.add(v); + if (world != null && world.isRemote && prevSeed != lastSeed) { + zmaster587.advancedRocketry.AdvancedRocketry.proxy.clearObservatoryScrollCache(); + } } @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); inv.writeToNBT(nbt); + + nbt.setLong("printedSetSeed", printedSetSeed); + if (!printedButtonsThisSeed.isEmpty()) { + int[] arr = printedButtonsThisSeed.stream().mapToInt(Integer::intValue).toArray(); + nbt.setIntArray("printedButtons", arr); + } return nbt; } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); - inv.readFromNBT(nbt); + + printedSetSeed = nbt.getLong("printedSetSeed"); + printedButtonsThisSeed.clear(); + int[] arr = nbt.getIntArray("printedButtons"); + if (arr != null) for (int v : arr) printedButtonsThisSeed.add(v); } + public LinkedList getDataBus() { return dataCables; } @@ -301,47 +425,83 @@ public List getModules(int ID, EntityPlayer player) { //ADD io slots modules.add(new ModuleTexturedSlotArray(5, 120, this, 1, 2, TextureResources.idChip)); modules.add(new ModuleOutputSlotArray(45, 120, this, 2, 3)); - modules.add(new ModuleProgress(25, 120, 0, new ProgressBarImage(217, 0, 17, 17, 234, 0, EnumFacing.DOWN, TextureResources.progressBars), this)); - modules.add(new ModuleButton(25, 120, 1, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonNull, LibVulpes.proxy.getLocalizedString("msg.observetory.text.processdiscovery"), 17, 17)); - ModuleButton scanButton = new ModuleButton(100, 120, 2, LibVulpes.proxy.getLocalizedString("msg.observetory.scan.button"), this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild, LibVulpes.proxy.getLocalizedString("msg.observetory.scan.tooltip"), 64, 18); - scanButton.setColor(extractData(dataConsumedPerRefresh, DataType.DISTANCE, EnumFacing.DOWN, false) == dataConsumedPerRefresh ? 0x00ff00 : 0xff0000); - modules.add(scanButton); + modules.add(new ModuleProgress(25, 120, 0, new ProgressBarImage(217, 0, 17, 17, 234, 0, EnumFacing.DOWN, TextureResources.progressBars), this)); + ModuleButton processBtn = new ModuleButton( + 25, 120, 1, "", + this, + zmaster587.libVulpes.inventory.TextureResources.buttonNull, + LibVulpes.proxy.getLocalizedString("msg.observetory.text.processdiscovery"), + 17, 17 + ); + + boolean alreadyPrinted = (lastButton != -1) && printedButtonsThisSeed.contains(lastButton); + + if (!isOpen) { + processBtn.setToolTipText(LibVulpes.proxy.getLocalizedString("msg.observetory.req.open")); + } else if (alreadyPrinted) { + processBtn.setToolTipText(LibVulpes.proxy.getLocalizedString("msg.observetory.print.already")); + } else { + processBtn.setToolTipText(LibVulpes.proxy.getLocalizedString("msg.observetory.text.processdiscovery")); + } + + modules.add(processBtn); List list2 = new LinkedList<>(); List buttonList = new LinkedList<>(); buttonType.clear(); - int g = 0; Asteroid asteroidSmol; if (lastButton != -1 && lastType != null && !lastType.isEmpty() && (asteroidSmol = ARConfiguration.getCurrentConfig().asteroidTypes.get(lastType)) != null) { List harvestList = asteroidSmol.getHarvest(lastSeed + lastButton, Math.max(1 - ((Math.min(getDataAmt(DataType.COMPOSITION), 2000) + Math.min(getDataAmt(DataType.MASS), 2000)) / 4000f), 0)); for (StackEntry entry : harvestList) { - //buttonList.add(new ModuleButton((g % 3)*24, 24*(g/3), -2, "",this, TextureResources.tabData, 24, 24)); - buttonList.add(new ModuleSlotButton((g % 2) * 24 + 1, 24 * (g / 2) + 1, -2, this, entry.stack, entry.midpoint + " +/- " + entry.variablility, getWorld())); - buttonList.add(new ModuleText((g % 2) * 24 + 1, 24 * (g / 2) + 1, entry.midpoint + "\n+/- " + entry.variablility, 0xFFFFFF, 0.5f)); + ItemStack s = entry.stack; + String tip = entry.midpoint + " +/- " + entry.variablility; + + int sx = (g % 2) * 24 + 1; + int sy = 24 * (g / 2) + 1; + + // If stack is empty, still show a slot button (optional), but don't crash. + if (!s.isEmpty() && Block.getBlockFromItem(s.getItem()) != Blocks.AIR) { + buttonList.add(new ModuleSlotButton(sx, sy, -2, this, s, tip, getWorld())); + } else { + buttonList.add(new ModuleItemSlotButton(sx, sy, -2, this, s, tip)); + } + + buttonList.add(new ModuleText(sx, sy, + entry.midpoint + "\n+/- " + entry.variablility, + 0xFFFFFF, 0.5f)); + g++; } float time = asteroidSmol.timeMultiplier; - buttonList.add(new ModuleText(0, 24 * (1 + (g / 2)), String.format("%s\n%.2fx", "Time:", time), 0x2f2f2f)); + String timeLabel = LibVulpes.proxy.getLocalizedString("msg.observetory.text.time"); + buttonList.add(new ModuleText( + 0, + 24 * (1 + (g / 2)), + String.format("%s\n%.2fx", timeLabel, time), + 0x2f2f2f + )); } - //Calculate Types int totalAmountAllowed = 10; float totalWeight = 0; + List keys = new ArrayList<>(ARConfiguration.getCurrentConfig().asteroidTypes.keySet()); + Collections.sort(keys); + List viableTypes = new LinkedList<>(); - for (String str : ARConfiguration.getCurrentConfig().asteroidTypes.keySet()) { + for (String str : keys) { Asteroid asteroid = ARConfiguration.getCurrentConfig().asteroidTypes.get(str); - if (asteroid.distance <= getMaxDistance()) { + if (asteroid != null && asteroid.distance <= getMaxDistance()) { totalWeight += asteroid.getProbability(); viableTypes.add(asteroid); } @@ -357,7 +517,6 @@ public List getModules(int ID, EntityPlayer player) { } } - for (int i = 0; i < finalList.size(); i++) { Asteroid asteroid = finalList.get(i); @@ -371,47 +530,61 @@ public List getModules(int ID, EntityPlayer player) { buttonType.put(i, asteroid.getName()); } - modules.add(new ModuleText(10, 18, LibVulpes.proxy.getLocalizedString("msg.observetory.text.asteroids"), 0x2d2d2d)); modules.add(new ModuleText(105, 18, LibVulpes.proxy.getLocalizedString("msg.observetory.text.composition"), 0x2d2d2d)); - //Ore display int baseX = 122; int baseY = 32; int sizeX = 52; int sizeY = 46; if (world.isRemote) { - //Border + // Border for RIGHT composition pane (unchanged) modules.add(new ModuleScaledImage(baseX - 3, baseY - 3, 3, baseY + sizeY + 6, TextureResources.verticalBar)); modules.add(new ModuleScaledImage(baseX + sizeX, baseY - 3, -3, baseY + sizeY + 6, TextureResources.verticalBar)); modules.add(new ModuleScaledImage(baseX, baseY - 3, sizeX, 3, TextureResources.horizontalBar)); modules.add(new ModuleScaledImage(baseX, 2 * baseY + sizeY, sizeX, -3, TextureResources.horizontalBar)); } - ModuleContainerPanYOnly pan2 = new ModuleContainerPanYOnly(baseX, baseY, buttonList, new LinkedList<>(), null, 40, 48, 0, 0, 0, 72); - modules.add(pan2); - - //Add borders for asteroid - baseX = 5; - baseY = 32; - sizeX = 112; - sizeY = 46; + // Preserve RIGHT pane coords before reusing baseX/baseY for the LEFT pane + final int compX = baseX; + final int compY = baseY; + final int compScreenX = 40; // same as original + final int compScreenY = 48; // same as original + + // ---- LEFT pane (asteroid list) border + baseX = 5; + baseY = 32; + sizeX = 112; + sizeY = 46; if (world.isRemote) { - //Border + // Border for LEFT asteroid list modules.add(new ModuleScaledImage(baseX - 3, baseY - 3, 3, baseY + sizeY + 6, TextureResources.verticalBar)); modules.add(new ModuleScaledImage(baseX + sizeX, baseY - 3, -3, baseY + sizeY + 6, TextureResources.verticalBar)); modules.add(new ModuleScaledImage(baseX, baseY - 3, sizeX, 3, TextureResources.horizontalBar)); modules.add(new ModuleScaledImage(baseX, 2 * baseY + sizeY, sizeX, -3, TextureResources.horizontalBar)); } - //Relying on a bug, is this safe? + // ---- LEFT asteroid list: wheel-enabled + cached if (lastSeed != -1) { - ModuleContainerPanYOnly pan = new ModuleContainerPanYOnly(baseX, baseY, list2, new LinkedList<>(), null, sizeX - 2, sizeY, 0, -48, 0, 72); - modules.add(pan); + modules.add(zmaster587.advancedRocketry.AdvancedRocketry.proxy + .createObservatoryAsteroidListPan(baseX, baseY, list2, sizeX, sizeY)); } + + // ---- RIGHT composition pane: parent class (drag-only; wheel will be 0 after left consumes it) + ModuleContainerPanYOnly panRight = new ModuleContainerPanYOnly( + compX, compY, + buttonList, new LinkedList<>(), + null, + compScreenX, compScreenY, + 0, 0, + 0, 72 + ); + modules.add(panRight); + + } else if (tabModule.getTab() == 0) { modules.add(new ModulePower(18, 20, getBatteries())); modules.add(toggleSwitch = new ModuleToggleSwitch(160, 5, 0, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonToggleImage, 11, 26, getMachineEnabled())); @@ -464,7 +637,15 @@ public void onInventoryButtonPressed(int buttonId) { super.onInventoryButtonPressed(buttonId); if (buttonId == 1) { - //Begin discovery processing + // Client: prevent packet spam + if (world != null && world.isRemote) { + boolean alreadyPrinted = (lastButton != -1) && printedButtonsThisSeed.contains(lastButton); + if (!isOpen || alreadyPrinted || lastButton == -1) { + return; + } + } + + // Server-side protection is in PROCESS_CHIP PacketHandler.sendToServer(new PacketMachine(this, PROCESS_CHIP)); } @@ -473,65 +654,128 @@ public void onInventoryButtonPressed(int buttonId) { lastType = buttonType.get(lastButton - LIST_OFFSET); PacketHandler.sendToServer(new PacketMachine(this, BUTTON_PRESS)); } - if (buttonId == 2) { - - //for(TileDataBus bus : getDataBus()) { - if (extractData(dataConsumedPerRefresh, DataType.DISTANCE, EnumFacing.UP, false) == dataConsumedPerRefresh) { - lastSeed = world.getTotalWorldTime() / 100; - lastButton = -1; - lastType = ""; - PacketHandler.sendToServer(new PacketMachine(this, SEED_CHANGE)); + if (buttonId == 2) { + if (world != null && world.isRemote) { + pendingReopenAfterSeedSync = true; } - //} + PacketHandler.sendToServer(new PacketMachine(this, SEED_CHANGE)); + } } @Override - public void useNetworkData(EntityPlayer player, Side side, byte id, - NBTTagCompound nbt) { + public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompound nbt) { super.useNetworkData(player, side, id, nbt); - if (id == -1) - storeData(-1); - if (id == -2) - loadData(-2); - else if (id == TAB_SWITCH && !world.isRemote) { - tabModule.setTab(nbt.getShort("tab")); - player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), getWorld(), pos.getX(), pos.getY(), pos.getZ()); - } else if (id == BUTTON_PRESS && !world.isRemote) { - lastButton = nbt.getShort("button"); - lastType = buttonType.get(lastButton - LIST_OFFSET); - markDirty(); - world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); - player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), getWorld(), pos.getX(), pos.getY(), pos.getZ()); - - } else if (id == SEED_CHANGE) { - if (extractData(dataConsumedPerRefresh, DataType.DISTANCE, EnumFacing.UP, false) >= dataConsumedPerRefresh) { - lastSeed = world.getTotalWorldTime() / 100; - lastButton = -1; - lastType = ""; - extractData(dataConsumedPerRefresh, DataType.DISTANCE, EnumFacing.UP, true); - world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); - markDirty(); - player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), getWorld(), pos.getX(), pos.getY(), pos.getZ()); + if (id == SYNC_PRINTED && world != null && world.isRemote) { + printedSetSeed = nbt.getLong("ps"); + + printedButtonsThisSeed.clear(); + for (int v : nbt.getIntArray("pb")) printedButtonsThisSeed.add(v); + + lastSeed = nbt.getLong("ls"); + lastButton = nbt.getInteger("lb"); + isOpen = nbt.getBoolean("io"); + return; + } + if (id == SYNC_SEED && world != null && world.isRemote) { + lastSeed = nbt.getLong("ls"); + lastButton = nbt.getInteger("lb"); + lastType = ""; // since scan resets it + isOpen = nbt.getBoolean("io"); + + zmaster587.advancedRocketry.AdvancedRocketry.proxy.clearObservatoryScrollCache(); + + if (pendingReopenAfterSeedSync) { + pendingReopenAfterSeedSync = false; + PacketHandler.sendToServer(new PacketMachine(this, REQUEST_REOPEN)); } + return; + } + if (id == -1) { storeData(-1); return; } + if (id == -2) { loadData(-2); return; } - } else if (id == PROCESS_CHIP && !world.isRemote) { + // --- server-side handlers only --- + if (!world.isRemote) { - if (inv.getStackInSlot(2).isEmpty() && isOpen && hasEnergy(500) && lastButton != -1) { - ItemStack stack = inv.decrStackSize(1, 1); - if (stack != ItemStack.EMPTY && stack.getItem() instanceof ItemAsteroidChip) { - ((ItemAsteroidChip) (stack.getItem())).setUUID(stack, lastSeed); - ((ItemAsteroidChip) (stack.getItem())).setType(stack, lastType); - ((ItemAsteroidChip) (stack.getItem())).setMaxData(stack, 1000); - inv.setInventorySlotContents(2, stack); + if (id == TAB_SWITCH) { + tabModule.setTab(nbt.getShort("tab")); + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); + } + else if (id == BUTTON_PRESS) { + lastButton = nbt.getShort("button"); + lastType = buttonType.get(lastButton - LIST_OFFSET); + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); + } + else if (id == SEED_CHANGE) { + if (extractData(dataConsumedPerRefresh, DataType.DISTANCE, EnumFacing.UP, false) >= dataConsumedPerRefresh) { + lastSeed = world.getTotalWorldTime() / 100; + lastButton = -1; + lastType = ""; + + printedButtonsThisSeed.clear(); + printedSetSeed = lastSeed; + PacketHandler.sendToPlayer(new PacketMachine(this, SYNC_SEED), player); + extractData(dataConsumedPerRefresh, DataType.DISTANCE, EnumFacing.UP, true); + markDirty(); + IBlockState st = world.getBlockState(pos); + world.notifyBlockUpdate(pos, st, st, 2); + } + } + else if (id == PROCESS_CHIP) { + + // Keep printed set aligned with current seed + if (printedSetSeed != lastSeed) { + printedButtonsThisSeed.clear(); + printedSetSeed = lastSeed; + } - extractData(1000, DataType.COMPOSITION, EnumFacing.UP, true); - extractData(1000, DataType.MASS, EnumFacing.UP, true); - useEnergy(500); + // Hard block duplicates for this scan + if (lastButton != -1 && printedButtonsThisSeed.contains(lastButton)) { + // No status message; just refresh UI so tooltip updates + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); + markDirty(); + if (player != null) { + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); + } + return; } + + if (inv.getStackInSlot(2).isEmpty() && isOpen && hasEnergy(500) && lastButton != -1) { + ItemStack stack = inv.decrStackSize(1, 1); + if (stack != ItemStack.EMPTY && stack.getItem() instanceof ItemAsteroidChip) { + ((ItemAsteroidChip)(stack.getItem())).setUUID(stack, lastSeed + lastButton); + ((ItemAsteroidChip)(stack.getItem())).setType(stack, lastType); + ((ItemAsteroidChip)(stack.getItem())).setMaxData(stack, 1000); + inv.setInventorySlotContents(2, stack); + + useEnergy(500); + + // Mark this selection as consumed for this seed + printedButtonsThisSeed.add(lastButton); + PacketHandler.sendToPlayer(new PacketMachine(this, SYNC_PRINTED), player); + + markDirty(); + world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2); + + // reopen 1 tick later to avoid cross-channel ordering race + ((WorldServer) world).addScheduledTask(() -> player.openGui( + LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ() + )); + } + } + } + else if (id == REQUEST_REOPEN) { + player.openGui(LibVulpes.instance, GuiHandler.guiId.MODULARNOINV.ordinal(), + getWorld(), pos.getX(), pos.getY(), pos.getZ()); } } } @@ -544,6 +788,22 @@ public void writeDataToNetwork(ByteBuf out, byte id) { out.writeShort(tabModule.getTab()); else if (id == BUTTON_PRESS) out.writeShort(lastButton); + + if (id == SYNC_PRINTED) { + out.writeLong(printedSetSeed); + out.writeInt(printedButtonsThisSeed.size()); + for (int b : printedButtonsThisSeed) out.writeInt(b); + out.writeLong(lastSeed); + out.writeInt(lastButton); + out.writeBoolean(isOpen); + } + if (id == SYNC_SEED) { + out.writeLong(lastSeed); + out.writeInt(lastButton); + out.writeBoolean(isOpen); + out.writeBoolean(getMachineEnabled()); + // lastType optional; you set it "" on scan, so you can skip it + } } @Override @@ -556,6 +816,22 @@ public void readDataFromNetwork(ByteBuf in, byte packetId, else if (packetId == BUTTON_PRESS) nbt.setShort("button", in.readShort()); + if (packetId == SYNC_PRINTED) { + nbt.setLong("ps", in.readLong()); + int n = in.readInt(); + int[] arr = new int[n]; + for (int i=0;i 0) { + // IMPORTANT: decrement the LOCAL chipData so the next bus + // doesn't see the original amount + chipData.removeData(accepted, true); + } + } - //dataItem.setData(dataChip, data.getData(), data.getData() != 0 ? data.getDataType() : DataType.UNDEFINED); + // Write the final state back to the item once + dataItem.setData(dataChip, chipData.getData(), chipData.getDataType()); + } } if (world.isRemote) { @@ -653,23 +976,37 @@ public void loadData(int id) { } } + @Override public void storeData(int id) { - int chipSlot = !inv.getStackInSlot(0).isEmpty() ? 0 : !inv.getStackInSlot(3).isEmpty() ? 3 : 4; - ItemStack dataChip = !inv.getStackInSlot(0).isEmpty() ? inv.getStackInSlot(0) : !inv.getStackInSlot(3).isEmpty() ? inv.getStackInSlot(3) : inv.getStackInSlot(4); + int chipSlot = !inv.getStackInSlot(0).isEmpty() ? 0 + : !inv.getStackInSlot(3).isEmpty() ? 3 + : 4; - if (dataChip != ItemStack.EMPTY && dataChip.getItem() instanceof ItemData && dataChip.getCount() == 1) { + ItemStack dataChip = !inv.getStackInSlot(0).isEmpty() ? inv.getStackInSlot(0) + : !inv.getStackInSlot(3).isEmpty() ? inv.getStackInSlot(3) + : inv.getStackInSlot(4); - ItemData dataItem = (ItemData) dataChip.getItem(); - DataStorage data = dataItem.getDataStorage(dataChip); + if (!dataChip.isEmpty() && dataChip.getItem() instanceof IDataItem && dataChip.getCount() == 1) { + + IDataItem dataItem = (IDataItem) dataChip.getItem(); + DataStorage chipData = dataItem.getDataStorage(dataChip); for (TileDataBus tile : dataCables) { - DataStorage.DataType dataType = tile.getDataObject().getDataType(); - if (doesSlotIndexMatchDataType(dataType, chipSlot)) - data.addData(tile.extractData(data.getMaxData() - data.getData(), data.getDataType(), EnumFacing.UP, true), dataType, true); + DataType busType = tile.getDataObject().getDataType(); + + if (!doesSlotIndexMatchDataType(busType, chipSlot)) continue; + + int remainingCap = chipData.getMaxData() - chipData.getData(); + if (remainingCap <= 0) break; + + int pulled = tile.extractData(remainingCap, chipData.getDataType(), EnumFacing.UP, true); + if (pulled > 0) { + chipData.addData(pulled, busType, true); + } } - dataItem.setData(dataChip, data.getData(), data.getDataType()); + dataItem.setData(dataChip, chipData.getData(), chipData.getDataType()); } if (world.isRemote) { @@ -677,6 +1014,7 @@ public void storeData(int id) { } } + @Override @Nullable public String getName() { @@ -709,6 +1047,36 @@ public void clear() { } + @Override + public void invalidate() { + super.invalidate(); + dataCables.clear(); + buttonType.clear(); + printedButtonsThisSeed.clear(); + printedSetSeed = -1; + lastSeed = -1; + lastButton = -1; + lastType = ""; + if (world != null && world.isRemote) { + zmaster587.advancedRocketry.AdvancedRocketry.proxy.clearObservatoryScrollCache(); + } + + + savedDataBusNbt.clear(); + } + + @Override + public void onChunkUnload() { + super.onChunkUnload(); + dataCables.clear(); + if (world != null && world.isRemote) { + zmaster587.advancedRocketry.AdvancedRocketry.proxy.clearObservatoryScrollCache(); + } + + + savedDataBusNbt.clear(); + } + @Override public void onModuleUpdated(ModuleBase module) { //ReopenUI on server diff --git a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/energy/TileMicrowaveReciever.java b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/energy/TileMicrowaveReciever.java index 9d33963a5..39f602d4f 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/energy/TileMicrowaveReciever.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/energy/TileMicrowaveReciever.java @@ -2,27 +2,34 @@ import io.netty.buffer.ByteBuf; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; import net.minecraft.network.NetworkManager; import net.minecraft.network.play.server.SPacketUpdateTileEntity; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.*; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; import zmaster587.advancedRocketry.api.SatelliteRegistry; import zmaster587.advancedRocketry.api.satellite.SatelliteBase; +import zmaster587.advancedRocketry.client.TooltipInjector; import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.advancedRocketry.dimension.DimensionProperties; import zmaster587.advancedRocketry.item.ItemSatelliteIdentificationChip; import zmaster587.advancedRocketry.stations.SpaceObjectManager; import zmaster587.advancedRocketry.stations.SpaceStationObject; +import zmaster587.advancedRocketry.util.PlanetaryTravelHelper; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.api.IUniversalEnergyTransmitter; import zmaster587.libVulpes.block.BlockMeta; @@ -30,15 +37,25 @@ import zmaster587.libVulpes.inventory.modules.ModuleText; import zmaster587.libVulpes.network.PacketHandler; import zmaster587.libVulpes.network.PacketMachine; +import zmaster587.libVulpes.tile.multiblock.TilePlaceholder; +import zmaster587.libVulpes.tile.multiblock.hatch.TileInventoryHatch; import zmaster587.libVulpes.tile.multiblock.TileMultiBlock; import zmaster587.libVulpes.tile.multiblock.TileMultiPowerProducer; import zmaster587.libVulpes.util.Vector3F; +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; + public class TileMicrowaveReciever extends TileMultiPowerProducer implements ITickable { + // key: BlockPos.toLong(), value: saved non-empty stacks for that hatch (slot order preserved) + private final Map> savedHatchInv = new HashMap<>(); + static final BlockMeta iron_block = new BlockMeta(AdvancedRocketryBlocks.blockSolarPanel); static final Object[][][] structure = new Object[][][]{ { @@ -113,20 +130,73 @@ public void onInventoryUpdated() { List list = new LinkedList<>(); - for (IInventory inv : itemInPorts) { + if (itemInPorts != null) { + for (IInventory inv : itemInPorts) { + for (int i = 0; i < inv.getSizeInventory(); i++) { + ItemStack stack = inv.getStackInSlot(i); + if (!stack.isEmpty() && stack.getItem() instanceof ItemSatelliteIdentificationChip) { + list.add(SatelliteRegistry.getSatelliteId(stack)); + } + } + } + } + connectedSatellites = new LinkedList<>(new LinkedHashSet<>(list)); + } + + private List getConnectedSatellitesLive() { + if (itemInPorts == null) return java.util.Collections.emptyList(); + + // refresh TE references (libVulpes replaces TEs during multiblock build/load) + List ports = getItemInPorts(); + + java.util.LinkedHashSet set = new java.util.LinkedHashSet<>(); + for (IInventory inv : ports) { + if (inv == null) continue; for (int i = 0; i < inv.getSizeInventory(); i++) { ItemStack stack = inv.getStackInSlot(i); if (!stack.isEmpty() && stack.getItem() instanceof ItemSatelliteIdentificationChip) { - list.add(SatelliteRegistry.getSatelliteId(stack)); + set.add(SatelliteRegistry.getSatelliteId(stack)); } } } + return new java.util.ArrayList<>(set); + } + @Override + public boolean attemptCompleteStructure(IBlockState state) { + if (!world.isRemote) { + // Snapshot BEFORE formation (real hatches) + snapshotHatchInventories(); + } + boolean ok = super.attemptCompleteStructure(state); + + if (!world.isRemote) { + if (ok) { + // Formation succeeded → push snapshot into placeholders (alive state) + writeSnapshotIntoPlaceholders(); + } else { + // Formation failed → discard + savedHatchInv.clear(); + } + } + return ok; + } - connectedSatellites = list; + @Override + public void deconstructMultiBlock(World worldIn, BlockPos destroyedPos, boolean blockBroken, IBlockState state) { + if (!worldIn.isRemote) { + snapshotFromPlaceholders(); + } + + super.deconstructMultiBlock(worldIn, destroyedPos, blockBroken, state); + + if (!worldIn.isRemote) { + restoreHatchInventories(); + } } + @Override public void update() { @@ -137,13 +207,38 @@ public void update() { } //Checks whenever a station changes dimensions or when the multiblock is intialized - ie any time the multipler could concieveably change - if (insolationPowerMultiplier == 0 || ((world.provider.getDimension() == ARConfiguration.getCurrentConfig().spaceDimId) && (powerSourceDimensionID != SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(this.pos).getOrbitingPlanetId()))) { - DimensionProperties properties = DimensionManager.getInstance().getDimensionProperties(world.provider.getDimension()); - insolationPowerMultiplier = (world.provider.getDimension() == ARConfiguration.getCurrentConfig().spaceDimId) ? SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(this.pos).getInsolationMultiplier() : properties.getPeakInsolationMultiplierWithoutAtmosphere(); - //Sets the ID of the place it's sourcing power from so it does not have to recheck - if (world.provider.getDimension() == ARConfiguration.getCurrentConfig().spaceDimId) - powerSourceDimensionID = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(this.pos).getOrbitingPlanetId(); + final int curDim = world.provider.getDimension(); + final int spaceDim = ARConfiguration.getCurrentConfig().spaceDimId; + + // Cache station once; can be null + final zmaster587.advancedRocketry.stations.SpaceStationObject station = + (curDim == spaceDim) + ? (zmaster587.advancedRocketry.stations.SpaceStationObject) + zmaster587.advancedRocketry.stations.SpaceObjectManager.getSpaceManager() + .getSpaceStationFromBlockCoords(this.pos) + : null; + + // Recompute when uninitialized OR (in space AND orbiting planet changed and station exists) + final boolean needRecompute = + (insolationPowerMultiplier == 0) + || (curDim == spaceDim && station != null && powerSourceDimensionID != station.getOrbitingPlanetId()); + + if (needRecompute) { + if (curDim == spaceDim && station != null) { + insolationPowerMultiplier = station.getInsolationMultiplier(); + powerSourceDimensionID = station.getOrbitingPlanetId(); + } else { + final zmaster587.advancedRocketry.dimension.DimensionProperties props = + zmaster587.advancedRocketry.dimension.DimensionManager.getInstance() + .getDimensionProperties(curDim); + insolationPowerMultiplier = (props != null) + ? props.getPeakInsolationMultiplierWithoutAtmosphere() + : 1.0; // safe fallback + powerSourceDimensionID = curDim; + } } + // If we're in space but station==null (early ticks), keep previous multiplier and carry on. + if (!isComplete()) return; @@ -174,37 +269,60 @@ public void update() { } } - DimensionProperties properties; - int dimId = world.provider.getDimension(); - SpaceStationObject spaceStation = (SpaceStationObject) SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(this.pos); - if (!world.isRemote && (DimensionManager.getInstance().isDimensionCreated(dimId) || world.provider.getDimension() == 0)) { - //This way we check to see if it's on a station, and if so, if it has any satellites in orbit around the planet the station is around to pull from - properties = (spaceStation != null) ? spaceStation.getOrbitingPlanet() : DimensionManager.getInstance().getDimensionProperties(dimId); + final int dimId = world.provider.getDimension(); + final boolean dimOk = DimensionManager.getInstance().isDimensionCreated(dimId) || dimId == 0; + + if (!world.isRemote && dimOk) { + // If we’re on a station, prefer its orbiting planet; otherwise use the local dim props + final SpaceStationObject stationHere = (dimId == ARConfiguration.getCurrentConfig().spaceDimId) + ? (SpaceStationObject) SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(this.pos) + : null; + + final DimensionProperties props = (stationHere != null) + ? stationHere.getOrbitingPlanet() + : DimensionManager.getInstance().getDimensionProperties(dimId); + int energyReceived = 0; - if (enabled) { - for (long lng : connectedSatellites) { - SatelliteBase satellite = properties.getSatellite(lng); - if (satellite instanceof IUniversalEnergyTransmitter) { - energyReceived += ((IUniversalEnergyTransmitter) satellite).transmitEnergy(EnumFacing.UP, false); + final List sats = enabled && props != null ? getConnectedSatellitesLive() : java.util.Collections.emptyList(); + + if (!sats.isEmpty()) { + for (long lng : sats) { + final SatelliteBase sat = props.getSatellite(lng); + if (sat == null) continue; + + final int satDim = sat.getDimensionId(); + final int hereDim = DimensionManager.getEffectiveDimId(world, pos).getId(); + if (!PlanetaryTravelHelper.isTravelAnywhereInPlanetarySystem(satDim, hereDim)) continue; + + if (sat instanceof IUniversalEnergyTransmitter) { + energyReceived += ((IUniversalEnergyTransmitter) sat).transmitEnergy(EnumFacing.UP, false); } } - //Multiplied by two for 520W = 1 RF/t becoming 2 RF/t @ 100% efficiency, and by insolation mult for solar stuff - energyReceived *= 2 * insolationPowerMultiplier; + // scale by insolation (your existing logic) + energyReceived = (int)Math.round(energyReceived * (2 * insolationPowerMultiplier)); } + + powerMadeLastTick = energyReceived; if (powerMadeLastTick != prevPowerMadeLastTick) { prevPowerMadeLastTick = powerMadeLastTick; - PacketHandler.sendToNearby(new PacketMachine(this, (byte) 1), world.provider.getDimension(), pos, 128); - + PacketHandler.sendToNearby(new PacketMachine(this, (byte) 1), + world.provider.getDimension(), pos, 128); } producePower(powerMadeLastTick); } - if (world.isRemote) - textModule.setText(LibVulpes.proxy.getLocalizedString("msg.microwaverec.generating") + " " + powerMadeLastTick + " " + LibVulpes.proxy.getLocalizedString("msg.powerunit.rfpertick")); - } + + if (world.isRemote) { + textModule.setText( + LibVulpes.proxy.getLocalizedString("msg.microwaverec.generating") + " " + + powerMadeLastTick + " " + + LibVulpes.proxy.getLocalizedString("msg.powerunit.rfpertick")); + } + } + @Override public SPacketUpdateTileEntity getUpdatePacket() { @@ -272,14 +390,28 @@ public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompou public NBTTagCompound writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); - int[] intArray = new int[connectedSatellites.size() * 2]; - - for (int i = 0; i < connectedSatellites.size() * 2; i += 2) { - intArray[i] = (connectedSatellites.get(i / 2)).intValue(); - intArray[i + 1] = (int) ((connectedSatellites.get(i / 2) >>> 32)); + // ---- saved hatch inventories ---- + NBTTagList hatchList = new NBTTagList(); + if (savedHatchInv != null && !savedHatchInv.isEmpty()) { + for (Map.Entry> e : savedHatchInv.entrySet()) { + NBTTagCompound entry = new NBTTagCompound(); + entry.setLong("pos", e.getKey()); + + NBTTagList items = new NBTTagList(); + NonNullList arr = e.getValue(); + for (int slot = 0; slot < arr.size(); slot++) { + ItemStack s = arr.get(slot); + if (s.isEmpty()) continue; + NBTTagCompound it = new NBTTagCompound(); + it.setInteger("slot", slot); + s.writeToNBT(it); + items.appendTag(it); + } + entry.setTag("items", items); + hatchList.appendTag(entry); + } } - - nbt.setIntArray("satilliteList", intArray); + nbt.setTag("savedHatchInv", hatchList); return nbt; } @@ -288,12 +420,279 @@ public NBTTagCompound writeToNBT(NBTTagCompound nbt) { public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); - int[] intArray = nbt.getIntArray("satilliteList"); - connectedSatellites.clear(); - for (int i = 0; i < intArray.length / 2; i += 2) { - connectedSatellites.add(intArray[i] | (((long) intArray[i + 1]) << 32)); + // ---- saved hatch inventories ---- + savedHatchInv.clear(); + + NBTTagList hatchList = nbt.getTagList("savedHatchInv", 10); + for (int i = 0; i < hatchList.tagCount(); i++) { + NBTTagCompound entry = hatchList.getCompoundTagAt(i); + long posKey = entry.getLong("pos"); + NBTTagList items = entry.getTagList("items", 10); + + int maxSlot = -1; + for (int j = 0; j < items.tagCount(); j++) { + int slot = items.getCompoundTagAt(j).getInteger("slot"); + if (slot > maxSlot) maxSlot = slot; + } + NonNullList arr = NonNullList.withSize(Math.max(maxSlot + 1, 1), ItemStack.EMPTY); + + for (int j = 0; j < items.tagCount(); j++) { + NBTTagCompound it = items.getCompoundTagAt(j); + int slot = it.getInteger("slot"); + arr.set(slot, new ItemStack(it)); + } + savedHatchInv.put(posKey, arr); + } + } + + // Push the pre-formation snapshot into the placeholders' replaced inventories (after formation) + private void writeSnapshotIntoPlaceholders() { + if (world == null || savedHatchInv.isEmpty()) return; + + final Object[][][] struct = getStructure(); + if (struct == null) return; + + final Vector3F off = getControllerOffset(struct); + final EnumFacing front = getFrontDirection(world.getBlockState(pos)); + + for (int y = 0; y < struct.length; y++) { + for (int z = 0; z < struct[0].length; z++) { + for (int x = 0; x < struct[0][0].length; x++) { + if (struct[y][z][x] == null) continue; + + int gx = pos.getX() + (x - off.x) * front.getFrontOffsetZ() - (z - off.z) * front.getFrontOffsetX(); + int gy = pos.getY() - y + off.y; + int gz = pos.getZ() - (x - off.x) * front.getFrontOffsetX() - (z - off.z) * front.getFrontOffsetZ(); + BlockPos bp = new BlockPos(gx, gy, gz); + + TileEntity te = world.getTileEntity(bp); + if (!(te instanceof TilePlaceholder)) continue; + + NonNullList snapshot = savedHatchInv.get(bp.toLong()); + if (snapshot == null || snapshot.isEmpty()) continue; + + TileEntity rep = ((TilePlaceholder) te).getReplacedTileEntity(); + if (!(rep instanceof IInventory)) continue; + + IInventory inv = (IInventory) rep; + + // First try to restore to original slots + for (int i = 0; i < snapshot.size(); i++) { + ItemStack src = snapshot.get(i); + if (src.isEmpty()) continue; + ItemStack cur = (i < inv.getSizeInventory()) ? inv.getStackInSlot(i) : ItemStack.EMPTY; + if (i < inv.getSizeInventory() && cur.isEmpty()) { + inv.setInventorySlotContents(i, src.copy()); + snapshot.set(i, ItemStack.EMPTY); + } + } + // Then merge leftovers + for (int i = 0; i < snapshot.size(); i++) { + ItemStack left = snapshot.get(i); + if (left.isEmpty()) continue; + + ItemStack rem = left.copy(); + // merge into existing stacks + for (int slot = 0; slot < inv.getSizeInventory() && !rem.isEmpty(); slot++) { + ItemStack dst = inv.getStackInSlot(slot); + if (dst.isEmpty()) continue; + if (ItemStack.areItemsEqual(dst, rem) && ItemStack.areItemStackTagsEqual(dst, rem)) { + int can = Math.min(inv.getInventoryStackLimit(), dst.getMaxStackSize()) - dst.getCount(); + if (can > 0) { + int move = Math.min(can, rem.getCount()); + dst.grow(move); + rem.shrink(move); + inv.setInventorySlotContents(slot, dst); + } + } + } + // fill empties + for (int slot = 0; slot < inv.getSizeInventory() && !rem.isEmpty(); slot++) { + if (inv.getStackInSlot(slot).isEmpty()) { + int put = Math.min(inv.getInventoryStackLimit(), rem.getMaxStackSize()); + ItemStack putStack = rem.splitStack(put); + inv.setInventorySlotContents(slot, putStack); + } + } + // any remainder stays in snapshot (shouldn’t normally happen) + snapshot.set(i, rem.isEmpty() ? ItemStack.EMPTY : rem); + } + inv.markDirty(); + } + } } + // After pushing into placeholders, discard snapshot + savedHatchInv.clear(); } + // Pull current contents back out of placeholders' replaced inventories (before teardown) + private void snapshotFromPlaceholders() { + savedHatchInv.clear(); + if (world == null) return; + + final Object[][][] struct = getStructure(); + if (struct == null) return; + + final Vector3F off = getControllerOffset(struct); + final EnumFacing front = getFrontDirection(world.getBlockState(pos)); + + for (int y = 0; y < struct.length; y++) { + for (int z = 0; z < struct[0].length; z++) { + for (int x = 0; x < struct[0][0].length; x++) { + if (struct[y][z][x] == null) continue; + + int gx = pos.getX() + (x - off.x) * front.getFrontOffsetZ() - (z - off.z) * front.getFrontOffsetX(); + int gy = pos.getY() - y + off.y; + int gz = pos.getZ() - (x - off.x) * front.getFrontOffsetX() - (z - off.z) * front.getFrontOffsetZ(); + BlockPos bp = new BlockPos(gx, gy, gz); + + TileEntity te = world.getTileEntity(bp); + + // Prefer the underlying hatch if this position is a placeholder + IInventory inv = null; + if (te instanceof TilePlaceholder) { + TileEntity rep = ((TilePlaceholder) te).getReplacedTileEntity(); + if (rep instanceof TileInventoryHatch) inv = (IInventory) rep; + } else if (te instanceof TileInventoryHatch) { + // Real multiblock component hatch (hidden block), still a live TE + inv = (IInventory) te; + } + + if (inv != null) { + NonNullList copy = NonNullList.withSize(inv.getSizeInventory(), ItemStack.EMPTY); + boolean any = false; + for (int i = 0; i < inv.getSizeInventory(); i++) { + ItemStack s = inv.getStackInSlot(i); + if (!s.isEmpty()) { + copy.set(i, s.copy()); + any = true; + } + } + if (any) savedHatchInv.put(bp.toLong(), copy); + } + } + } + } + } + + + + + private void snapshotHatchInventories() { + savedHatchInv.clear(); + final Object[][][] struct = getStructure(); + if (struct == null || world == null) return; + + final Vector3F off = getControllerOffset(struct); + final EnumFacing front = getFrontDirection(world.getBlockState(pos)); + + for (int y = 0; y < struct.length; y++) { + for (int z = 0; z < struct[0].length; z++) { + for (int x = 0; x < struct[0][0].length; x++) { + if (struct[y][z][x] == null) continue; + + int gx = pos.getX() + (x - off.x) * front.getFrontOffsetZ() - (z - off.z) * front.getFrontOffsetX(); + int gy = pos.getY() - y + off.y; + int gz = pos.getZ() - (x - off.x) * front.getFrontOffsetX() - (z - off.z) * front.getFrontOffsetZ(); + BlockPos bp = new BlockPos(gx, gy, gz); + + if (!world.getChunkFromBlockCoords(bp).isLoaded()) continue; + + TileEntity te = world.getTileEntity(bp); + + // If already replaced, pull from the placeholder’s replaced tile + if (te instanceof TilePlaceholder) te = ((TilePlaceholder) te).getReplacedTileEntity(); + + if (te instanceof IInventory) { + IInventory inv = (IInventory) te; + NonNullList copy = NonNullList.withSize(inv.getSizeInventory(), ItemStack.EMPTY); + boolean any = false; + for (int i = 0; i < inv.getSizeInventory(); i++) { + ItemStack s = inv.getStackInSlot(i); + if (!s.isEmpty()) { + copy.set(i, s.copy()); + any = true; + } + } + if (any) savedHatchInv.put(bp.toLong(), copy); + } + } + } + } + } + + private void restoreHatchInventories() { + if (world == null || savedHatchInv.isEmpty()) return; + + for (Map.Entry> e : savedHatchInv.entrySet()) { + BlockPos bp = BlockPos.fromLong(e.getKey()); + TileEntity te = world.getTileEntity(bp); + + // If placeholder is still present for any reason, restore into the underlying replaced tile + if (te instanceof TilePlaceholder) te = ((TilePlaceholder) te).getReplacedTileEntity(); + + if (te instanceof IInventory) { + IInventory inv = (IInventory) te; + NonNullList items = e.getValue(); + + // naive merge: try to put stacks back in their original slots first, then merge to any slot + // 1) original slots + for (int i = 0; i < items.size(); i++) { + ItemStack src = items.get(i); + if (src.isEmpty()) continue; + ItemStack cur = inv.getStackInSlot(i); + if (cur.isEmpty()) { + inv.setInventorySlotContents(i, src.copy()); + items.set(i, ItemStack.EMPTY); + } + } + // 2) merge leftovers anywhere they fit, otherwise drop + for (int i = 0; i < items.size(); i++) { + ItemStack left = items.get(i); + if (left.isEmpty()) continue; + + ItemStack rem = left.copy(); + // try merging into existing stacks + for (int slot = 0; slot < inv.getSizeInventory() && !rem.isEmpty(); slot++) { + ItemStack dst = inv.getStackInSlot(slot); + if (dst.isEmpty()) continue; + if (ItemStack.areItemsEqual(dst, rem) && ItemStack.areItemStackTagsEqual(dst, rem)) { + int can = Math.min(inv.getInventoryStackLimit(), dst.getMaxStackSize()) - dst.getCount(); + if (can > 0) { + int move = Math.min(can, rem.getCount()); + dst.grow(move); + rem.shrink(move); + inv.setInventorySlotContents(slot, dst); + } + } + } + // fill empty slots + for (int slot = 0; slot < inv.getSizeInventory() && !rem.isEmpty(); slot++) { + if (inv.getStackInSlot(slot).isEmpty()) { + int put = Math.min(inv.getInventoryStackLimit(), rem.getMaxStackSize()); + ItemStack putStack = rem.splitStack(put); + inv.setInventorySlotContents(slot, putStack); + } + } + // drop remainder to world + if (!rem.isEmpty()) { + world.spawnEntity(new EntityItem(world, bp.getX() + 0.5, bp.getY() + 0.5, bp.getZ() + 0.5, rem)); + } + items.set(i, ItemStack.EMPTY); + } + inv.markDirty(); + } else { + // no inventory to restore into → drop all + for (ItemStack s : e.getValue()) { + if (!s.isEmpty()) { + world.spawnEntity(new EntityItem(world, bp.getX() + 0.5, bp.getY() + 0.5, bp.getZ() + 0.5, s.copy())); + } + } + } + } + savedHatchInv.clear(); + } + + } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/orbitallaserdrill/TileOrbitalLaserDrill.java b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/orbitallaserdrill/TileOrbitalLaserDrill.java index 9e3ea86de..1191731a6 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/orbitallaserdrill/TileOrbitalLaserDrill.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/orbitallaserdrill/TileOrbitalLaserDrill.java @@ -92,8 +92,14 @@ public class TileOrbitalLaserDrill extends TileMultiPowerConsumer implements IGu private boolean terraformingstatus; boolean client_first_loop = true; // for render bug on client //private Ticket ticket; // this is useless anyway because it would not load the energy supply system and the laser would run out of energy - + + // Performance tweaks + private int lastTfDim = Integer.MIN_VALUE; + private boolean voidCobble; + private ModuleButton voidCobbleBtn; int last_orbit_dim; + private final boolean voidMiningMode; + TerraformingHelper t; WorldServer orbitWorld; @@ -101,6 +107,8 @@ public class TileOrbitalLaserDrill extends TileMultiPowerConsumer implements IGu public TileOrbitalLaserDrill() { super(); + this.voidMiningMode = !ARConfiguration.getCurrentConfig().laserDrillPlanet; + terraformingstatus = false; client_first_loop = true; @@ -109,13 +117,33 @@ public TileOrbitalLaserDrill() { yCenter = 0; numSteps = 0; prevDir = null; - resetBtn = new ModuleButton(40, 20, 2, LibVulpes.proxy.getLocalizedString("msg.spacelaser.reset"), this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild, 34, 20); + + resetBtn = new ModuleButton( + 40, 20, 2, + LibVulpes.proxy.getLocalizedString("msg.spacelaser.reset"), + this, + zmaster587.libVulpes.inventory.TextureResources.buttonBuild, + 34, 20 + ); + + // Only meaningful in void-mining mode (from config) + voidCobbleBtn = new ModuleButton( + 50, 60, + 3, // buttonId + LibVulpes.proxy.getLocalizedString("msg.spacelaser.voidcobble"), + this, + zmaster587.libVulpes.inventory.TextureResources.buttonBuild, + 85, 20 + ); + positionText = new ModuleText(83, 63, "empty... shit!", 0x0b0b0b); updateText = new ModuleText(83, 63, "also empty...", 0x0b0b0b); xtext = new ModuleText(83, 33, "X:", 0x0b0b0b); ztext = new ModuleText(83, 43, "Z:", 0x0b0b0b); - no_targets_text = new ModuleText(21, 43, "", 0x0b0b0b); - no_targets_text.setText("No target found!\nGo down and survey the area!"); + String ntLine1 = LibVulpes.proxy.getLocalizedString("msg.spacelaser.notarget1"); + String ntLine2 = LibVulpes.proxy.getLocalizedString("msg.spacelaser.notarget2"); + String ntText = ntLine1 + "\n" + ntLine2; + no_targets_text = new ModuleText(21, 43, ntText, 0x0b0b0b); locationX = new ModuleNumericTextbox(this, 93, 31, 50, 10, 16); locationZ = new ModuleNumericTextbox(this, 93, 41, 50, 10, 16); tickSinceLastOperation = 0; @@ -127,6 +155,7 @@ public TileOrbitalLaserDrill() { this.miningDrill = new MiningDrill(); else this.miningDrill = new VoidDrill(); + this.terraformingDrill = new terraformingdrill(); this.drill = miningDrill; @@ -134,8 +163,13 @@ public TileOrbitalLaserDrill() { finished = false; isJammed = false; mode = MODE.SINGLE; + + // If we ever need to set initial voidCobble from config, do it here + updateVoidCobbleButtonVisuals(); } + + @Override public Object[][][] getStructure() { return structure; @@ -172,7 +206,8 @@ public void writeDataToNetwork(ByteBuf out, byte id) { if (id == 15) { out.writeInt(this.laserX); out.writeInt(this.laserZ); - }else if (id == 11){ + } + else if (id == 11){ out.writeInt(mode.ordinal()); out.writeInt(this.xCenter); out.writeInt(this.yCenter); @@ -180,6 +215,7 @@ public void writeDataToNetwork(ByteBuf out, byte id) { out.writeInt(this.laserZ); out.writeBoolean(this.isRunning); out.writeBoolean(terraformingstatus); + out.writeBoolean(voidCobble); } else if (id == 12) { out.writeBoolean(isRunning); @@ -209,6 +245,7 @@ else if (id == 11){ nbt.setInteger("currentZ", in.readInt()); nbt.setBoolean("isRunning", in.readBoolean()); nbt.setBoolean("terraformingstatus", in.readBoolean()); + nbt.setBoolean("voidCobble", in.readBoolean()); } else if (id == 12) { nbt.setBoolean("isRunning", in.readBoolean()); @@ -233,76 +270,93 @@ public void client_update_tf_info(){ @Override public void useNetworkData(EntityPlayer player, Side side, byte id, - NBTTagCompound nbt) { + NBTTagCompound nbt) { super.useNetworkData(player, side, id, nbt); + if (id == 15) { laserZ = nbt.getInteger("currentZ"); laserX = nbt.getInteger("currentX"); - positionText.setText("position:\n"+this.laserX+" : "+this.laserZ); - }else if (id == 11){ + positionText.setText("position:\n" + this.laserX + " : " + this.laserZ); + } else if (id == 11) { resetSpiral(); this.isRunning = nbt.getBoolean("isRunning"); mode = MODE.values()[nbt.getInteger("mode")]; + if (voidMiningMode && mode != MODE.SINGLE) { + mode = MODE.SINGLE; + } xCenter = nbt.getInteger("newX"); yCenter = nbt.getInteger("newZ"); laserZ = nbt.getInteger("currentZ"); laserX = nbt.getInteger("currentX"); - positionText.setText("position:\n"+this.laserX+" : "+this.laserZ); + positionText.setText("position:\n" + this.laserX + " : " + this.laserZ); updateText.setText(this.getMode().toString()); locationX.setText(String.valueOf(this.xCenter)); locationZ.setText(String.valueOf(this.yCenter)); - //System.out.println("reset client:"+xCenter+":"+yCenter+":"+mode); resetBtn.setColor(0xf0f0f0); check_is_terraforming_update_gui(); this.terraformingstatus = nbt.getBoolean("terraformingstatus"); + this.voidCobble = nbt.getBoolean("voidCobble"); client_update_tf_info(); - //System.out.println("is running: "+ isRunning); - } - else if (id == 12) { + if (voidCobbleBtn != null) { + updateVoidCobbleButtonVisuals(); + } + + } else if (id == 12) { this.isRunning = nbt.getBoolean("isRunning"); - } - else if (id == 16){ + } else if (id == 16) { this.terraformingstatus = nbt.getBoolean("terraformingstatus"); client_update_tf_info(); + } else if (id == 14) { + resetSpiral(); + mode = MODE.values()[nbt.getInteger("mode")]; + xCenter = nbt.getInteger("newX"); + yCenter = nbt.getInteger("newZ"); + laserZ = yCenter; + laserX = xCenter; - } - else if (id == 14){ - resetSpiral(); - mode = MODE.values()[nbt.getInteger("mode")]; - xCenter = nbt.getInteger("newX"); - yCenter = nbt.getInteger("newZ"); - laserZ = yCenter; - laserX = xCenter; - //System.out.println("reset:"+xCenter+":"+yCenter+":"+mode); - // do all the reset stuff if (drill != null) { drill.deactivate(); } finished = false; setRunning(false); - if (mode == MODE.T_FORM){ + if (mode == MODE.T_FORM) { this.drill = this.terraformingDrill; - }else { + } else { this.drill = this.miningDrill; } - checkjam(); - checkCanRun(); - //update clients on new data - PacketHandler.sendToNearby(new PacketMachine(this, (byte) 11), this.world.provider.getDimension(), pos, 2048); - } - else if (id == 13) - //update clients on new data - PacketHandler.sendToNearby(new PacketMachine(this, (byte) 11), this.world.provider.getDimension(), pos, 2048); + checkjam(); + checkCanRun(); + PacketHandler.sendToNearby(new PacketMachine(this, (byte) 11), + this.world.provider.getDimension(), pos, 2048); + + } else if (id == 13) { + PacketHandler.sendToNearby(new PacketMachine(this, (byte) 11), + this.world.provider.getDimension(), pos, 2048); + + } else if (id == 17) { + // **IMPORTANT**: only act on server + if (!world.isRemote) { + this.voidCobble = !this.voidCobble; + if (miningDrill instanceof VoidDrill) { + ((VoidDrill) miningDrill).setVoidCobble(voidCobble); + } + // push new state to clients + PacketHandler.sendToNearby(new PacketMachine(this, (byte) 11), + this.world.provider.getDimension(), pos, 2048); + markDirty(); + } + } markDirty(); } + public void transferItems(IInventory inventorySource, IItemHandler inventoryTarget) { for (int i = 0; i < inventorySource.getSizeInventory(); i++) { ItemStack stack = inventorySource.getStackInSlot(i).copy(); @@ -430,7 +484,7 @@ public void update() { tickSinceLastOperation++; - if (mode != MODE.T_FORM) { + if (mode != MODE.T_FORM && isJammed) { checkjam(); } checkCanRun(); @@ -513,7 +567,10 @@ public void onDestroy() { if (this.drill != null) { this.drill.deactivate(); } - //ForgeChunkManager.releaseTicket(ticket); + orbitWorld = null; + t = null; + last_orbit_dim = 0; + lastTfDim = Integer.MIN_VALUE; } @Override @@ -522,6 +579,10 @@ public void onChunkUnload() { this.drill.deactivate(); } isRunning = false; + orbitWorld = null; + t = null; + last_orbit_dim = 0; + lastTfDim = Integer.MIN_VALUE; } @Override @@ -541,7 +602,7 @@ public NBTTagCompound writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); - + nbt.setBoolean("voidCobble", voidCobble); nbt.setInteger("laserX", laserX); nbt.setInteger("laserZ", laserZ); nbt.setByte("mode", (byte) mode.ordinal()); @@ -566,6 +627,10 @@ public void readFromNBT(NBTTagCompound nbt) { laserX = nbt.getInteger("laserX"); laserZ = nbt.getInteger("laserZ"); mode = MODE.values()[nbt.getByte("mode")]; + // If config says we are in void-mining mode, force SINGLE + if (voidMiningMode && mode != MODE.SINGLE) { + mode = MODE.SINGLE; + } this.isJammed = nbt.getBoolean("jammed"); xCenter = nbt.getInteger("CenterX"); @@ -582,18 +647,19 @@ public void readFromNBT(NBTTagCompound nbt) { }else { this.drill = this.miningDrill; } - } - + voidCobble = nbt.getBoolean("voidCobble"); + if (miningDrill instanceof VoidDrill) { + ((VoidDrill) miningDrill).setVoidCobble(voidCobble); + } + } + /** * Take items from internal inventory */ public void checkjam() { - - + // Only called when isJammed == true if (this.one_hatch_empty()) { this.isJammed = false; - }else{ - this.isJammed = true; } } @@ -626,7 +692,20 @@ private boolean unableToRun() { public void checkCanRun() { - if (world.isRemote) return; // client has no business here + if (world.isRemote) return; + + // Read redstone once and reuse it + final int redstonePower = world.isBlockIndirectlyGettingPowered(getPos()); + + // Fast path for void-mining: if there is no redstone, don't even bother + // with space station / dimension logic. + if (voidMiningMode && redstonePower == 0) { + if (isRunning) { + drill.deactivate(); + setRunning(false); + } + return; + } ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(this.pos); if(spaceObject == null){ @@ -638,54 +717,53 @@ public void checkCanRun() { } int orbitDimId = spaceObject.getOrbitingPlanetId(); - if (orbitDimId != last_orbit_dim ||orbitWorld== null || t == null){ + // Ensure orbitWorld exists when you might activate a drill + if (orbitDimId != last_orbit_dim || orbitWorld == null) { last_orbit_dim = orbitDimId; + if (!DimensionManager.isDimensionRegistered(orbitDimId)) { - if (isRunning) { - drill.deactivate(); - setRunning(false); - } + if (isRunning) { drill.deactivate(); setRunning(false); } return; } - orbitWorld = DimensionManager.getWorld(orbitDimId); + orbitWorld = DimensionManager.getWorld(orbitDimId); if (orbitWorld == null) { DimensionManager.initDimension(orbitDimId); orbitWorld = DimensionManager.getWorld(orbitDimId); if (orbitWorld == null) { - if (isRunning) { - drill.deactivate(); - setRunning(false); - } + if (isRunning) { drill.deactivate(); setRunning(false); } return; } } - t = terraformingDrill.get_my_helper(orbitWorld); } + if (mode == MODE.T_FORM) { + if (t == null || lastTfDim != orbitDimId) { + t = terraformingDrill.get_my_helper(orbitWorld); + lastTfDim = orbitDimId; + } - - if (!t.has_blocks_in_tf_queue()) { - if (terraformingstatus) { - terraformingstatus = false; + boolean hasQueue = t != null && t.has_blocks_in_tf_queue(); + if (terraformingstatus != hasQueue) { + terraformingstatus = hasQueue; PacketHandler.sendToAll(new PacketMachine(this, (byte) 16)); - } } else { - if (!terraformingstatus) { - terraformingstatus = true; + if (terraformingstatus) { + terraformingstatus = false; PacketHandler.sendToAll(new PacketMachine(this, (byte) 16)); } } + //Laser redstone power, not be jammed, and be in orbit and energy to function - if ((mode == MODE.T_FORM && (t==null ||!t.has_blocks_in_tf_queue())) || this.finished || (this.isJammed && mode != MODE.T_FORM) || world.isBlockIndirectlyGettingPowered(getPos()) == 0 || unableToRun()) { + if ((mode == MODE.T_FORM && (t==null ||!t.has_blocks_in_tf_queue())) || this.finished || (this.isJammed && mode != MODE.T_FORM) || redstonePower == 0 || unableToRun()) { if (isRunning) { drill.deactivate(); setRunning(false); } - } else if (world.isBlockIndirectlyGettingPowered(getPos()) > 0) { + } else if (redstonePower > 0) { if (orbitDimId == SpaceObjectManager.WARPDIMID) @@ -769,39 +847,76 @@ public void onModuleUpdated(ModuleBase module) { } + private void updateVoidCobbleButtonVisuals() { + if (voidCobbleBtn == null) return; + + String key = voidCobble + ? "msg.spacelaser.voidcobble.on" + : "msg.spacelaser.voidcobble.off"; + + voidCobbleBtn.setText(LibVulpes.proxy.getLocalizedString(key)); + voidCobbleBtn.setColor(voidCobble ? 0x90ff90 : 0xf0f0f0); + } + + + + @Override public List getModules(int id, EntityPlayer player) { List modules = new LinkedList<>(); + // --- VOID-MINING SIMPLIFIED GUI --- + if (voidMiningMode) { + if (world.isRemote) { + // Ask server for current state (mode, running, voidCobble, etc.) + PacketHandler.sendToServer(new PacketMachine(this, (byte) 13)); + + // Lore text: two lines, joined with '\n' in Java + String line1 = LibVulpes.proxy.getLocalizedString("msg.spacelaser.voidmining.line1"); + String line2 = LibVulpes.proxy.getLocalizedString("msg.spacelaser.voidmining.line2"); + String lore = line1 + "\n" + line2; + + modules.add(new ModuleText(35, 30, lore, 0x0b0b0b)); + + // Void cobble toggle button + updateVoidCobbleButtonVisuals(); + modules.add(voidCobbleBtn); + } + + // Power bar is still useful + modules.add(new ModulePower(11, 25, batteries)); + return modules; + } + + // --- ORIGINAL PLANET-MINING GUI --- if (world.isRemote) { //request update on information PacketHandler.sendToServer(new PacketMachine(this, (byte) 13)); modules.add(updateText = new ModuleText(110, 20, this.getMode().toString(), 0x0b0b0b, true)); - modules.add(locationX); modules.add(locationZ); - modules.add(xtext); modules.add(ztext); modules.add(no_targets_text); - modules.add(positionText); - - //modules.add(new ModuleImage(8, 16, TextureResources.laserGuiBG)); + // modules.add(new ModuleImage(8, 16, TextureResources.laserGuiBG)); } - modules.add(new ModuleButton(83, 20, 0, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonLeft, 5, 8)); - modules.add(new ModuleButton(137, 20, 1, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonRight, 5, 8)); + modules.add(new ModuleButton(83, 20, 0, "", this, + zmaster587.libVulpes.inventory.TextureResources.buttonLeft, 5, 8)); + modules.add(new ModuleButton(137, 20, 1, "", this, + zmaster587.libVulpes.inventory.TextureResources.buttonRight, 5, 8)); modules.add(resetBtn); modules.add(new ModulePower(11, 25, batteries)); return modules; } + @Override public String getModularInventoryName() { return "tile.spaceLaser.name"; @@ -845,10 +960,26 @@ public void onInventoryButtonPressed(int buttonId) { } else if (buttonId == 2) { PacketHandler.sendToServer(new PacketMachine(this, (byte) 14)); return; + } else if (buttonId == 3) { + // Ask server to toggle voidCobble (no payload needed) + PacketHandler.sendToServer(new PacketMachine(this, (byte) 17)); + return; } else return; } + @Override + public void invalidate() { + super.invalidate(); + + if (!world.isRemote) { + if (drill != null) { + drill.deactivate(); + } + isRunning = false; + } + } + @Override @SideOnly(Side.CLIENT) public double getMaxRenderDistanceSquared() { @@ -861,4 +992,4 @@ public enum MODE { SPIRAL, T_FORM } -} \ No newline at end of file +} diff --git a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/orbitallaserdrill/VoidDrill.java b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/orbitallaserdrill/VoidDrill.java index 89a2ae7f8..510ef554d 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/multiblock/orbitallaserdrill/VoidDrill.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/multiblock/orbitallaserdrill/VoidDrill.java @@ -21,76 +21,90 @@ */ class VoidDrill extends AbstractDrill { - private final Random random; - private List ores; - private boolean planetOresInitialized; + private final Random random = new Random(); + private final List ores = new ArrayList<>(); + private boolean voidCobble; // performance optimization: if true, cobble is not even generated + private int opCounter = 0; // counts operations when voidCobble is true + private static final ItemStack[] EMPTY = new ItemStack[0]; VoidDrill() { - this.random = new Random(); - this.planetOresInitialized = false; loadGlobalOres(); } + void setVoidCobble(boolean voidCobble) { + this.voidCobble = voidCobble; + } + private void loadGlobalOres() { - //isEmpty check because is called in post init to register for holo projector - if (ores == null && !ARConfiguration.getCurrentConfig().standardLaserDrillOres.isEmpty()) { - ores = new ArrayList<>(); + ores.clear(); - for (int i = 0; i < ARConfiguration.getCurrentConfig().standardLaserDrillOres.size(); i++) { - String oreDictName = ARConfiguration.getCurrentConfig().standardLaserDrillOres.get(i); + // isEmpty check because is called in post init to register for holo projector + List configOres = ARConfiguration.getCurrentConfig().standardLaserDrillOres; + if (configOres == null || configOres.isEmpty()) { + return; // we'll handle empty list gracefully in performOperation + } - String[] args = oreDictName.split(":"); + for (String oreDictName : configOres) { + if (oreDictName == null || oreDictName.isEmpty()) { + continue; + } - List globalOres = OreDictionary.getOres(args[0]); + String[] args = oreDictName.split(":"); - if (globalOres != null && !globalOres.isEmpty()) { - int amt = 1; - if (args.length > 1) { - try { - amt = Integer.parseInt(args[1]); - } catch (NumberFormatException ignored) { - } - } - ores.add(new ItemStack(globalOres.get(0).getItem(), amt, globalOres.get(0).getItemDamage())); - } else { - String[] splitStr = oreDictName.split(":"); - String name; + // First try ore dictionary entry (e.g. "oreIron:2") + List globalOres = OreDictionary.getOres(args[0]); + if (globalOres != null && !globalOres.isEmpty()) { + int amt = 1; + if (args.length > 1) { try { - name = splitStr[0] + ":" + splitStr[1]; - } catch (IndexOutOfBoundsException e) { - AdvancedRocketry.logger.warn("Unexpected ore name: \"" + oreDictName + "\" during laser drill harvesting"); - continue; + amt = Integer.parseInt(args[1]); + } catch (NumberFormatException ignored) { } + } + ItemStack base = globalOres.get(0); + ores.add(new ItemStack(base.getItem(), amt, base.getItemDamage())); + continue; + } - int meta = 0; - int size = 1; - //format: "name meta size" - if (splitStr.length > 2) { - try { - meta = Integer.parseInt(splitStr[2]); - } catch (NumberFormatException ignored) { - } - } - if (splitStr.length > 3) { - try { - size = Integer.parseInt(splitStr[3]); - } catch (NumberFormatException ignored) { - } - } + // Fallback: "modid:blockname[:meta[:size]]" + String[] splitStr = oreDictName.split(":"); + String name; + try { + name = splitStr[0] + ":" + splitStr[1]; + } catch (IndexOutOfBoundsException e) { + AdvancedRocketry.logger.warn("Unexpected ore name: \"{}\" during laser drill harvesting", oreDictName); + continue; + } - ItemStack stack = ItemStack.EMPTY; - Block block = Block.getBlockFromName(name); - if (block == null) { - Item item = Item.getByNameOrId(name); - if (item != null) - stack = new ItemStack(item, size, meta); - } else - stack = new ItemStack(block, size, meta); - - if (!stack.isEmpty()) - ores.add(stack); + int meta = 0; + int size = 1; + if (splitStr.length > 2) { + try { + meta = Integer.parseInt(splitStr[2]); + } catch (NumberFormatException ignored) { + } + } + if (splitStr.length > 3) { + try { + size = Integer.parseInt(splitStr[3]); + } catch (NumberFormatException ignored) { } } + + ItemStack stack = ItemStack.EMPTY; + Block block = Block.getBlockFromName(name); + if (block == null) { + Item item = Item.getByNameOrId(name); + if (item != null) { + stack = new ItemStack(item, size, meta); + } + } else { + stack = new ItemStack(block, size, meta); + } + + if (!stack.isEmpty()) { + ores.add(stack); + } } } @@ -99,41 +113,85 @@ private void loadGlobalOres() { * * @return The ItemStacks produced by this tick of drilling */ + @Override ItemStack[] performOperation() { - ArrayList items = new ArrayList<>(); - if (random.nextInt(10) == 0) { - ItemStack item = ores.get(random.nextInt(ores.size())); - ItemStack newStack = item.copy(); - items.add(newStack); - } else - items.add(new ItemStack(Blocks.COBBLESTONE, 1)); - ItemStack[] stacks = new ItemStack[items.size()]; + // --- VOID-COBBLE MODE: only ores, every 10th operation --- + if (voidCobble) { + if (ores.isEmpty()) { + // No configured ores -> nothing to give + return EMPTY; + } - stacks = items.toArray(stacks); + opCounter++; + // 9 out of 10 operations: no items at all + if (opCounter % 10 != 0) { + return EMPTY; + } + + // 10th operation: roll one ore stack + ItemStack[] result = new ItemStack[1]; + ItemStack template = ores.get(random.nextInt(ores.size())); + result[0] = template.copy(); + return result; + } + + // --- NORMAL MODE: 10% ore, 90% cobble (old behavior) --- + + // 10% ore + boolean produceOre = !ores.isEmpty() && random.nextInt(10) == 0; - return stacks; + if (produceOre) { + ItemStack[] result = new ItemStack[1]; + ItemStack template = ores.get(random.nextInt(ores.size())); + result[0] = template.copy(); + return result; + } + + // Cobble case + ItemStack[] result = new ItemStack[1]; + result[0] = new ItemStack(Blocks.COBBLESTONE, 1); + return result; } + + + @Override boolean activate(World world, int x, int z) { - // Ideally, this should be done in the constructor, but the world provider is null there for reasons unknown, so this gets delayed until first activation - this.ores = null; - this.planetOresInitialized = false; + if (world == null) { + return false; + } + + // Rebuild base list from config loadGlobalOres(); - DimensionProperties dimProperties = DimensionManager.getInstance().getDimensionProperties(world.provider.getDimension()); - ores.addAll(dimProperties.laserDrillOres.stream().filter(s -> !ores.contains(s)).collect(Collectors.toSet())); - this.planetOresInitialized = true; + opCounter = 0; // reset when drill is (re)started + + DimensionProperties dimProperties = + DimensionManager.getInstance().getDimensionProperties(world.provider.getDimension()); + + if (dimProperties != null && dimProperties.laserDrillOres != null) { + for (ItemStack s : dimProperties.laserDrillOres) { + if (s != null && !ores.contains(s)) { + ores.add(s); + } + } + } return true; } + + @Override void deactivate() { + // No state required } + @Override boolean isFinished() { return false; } + @Override boolean needsRestart() { return false; } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileSatelliteBuilder.java b/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileSatelliteBuilder.java index 3d8847b2c..476bb483a 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileSatelliteBuilder.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileSatelliteBuilder.java @@ -198,7 +198,8 @@ public List getModules(int ID, EntityPlayer player) { modules.add(new ModuleTexturedSlotArray(58, 16, this, chipSlot, chipSlot + 1, TextureResources.idChip)); // Id chip modules.add(new ModuleTexturedSlotArray(82, 16, this, chipCopySlot, chipCopySlot + 1, TextureResources.idChip)); // Id chip modules.add(new ModuleProgress(75, 36, 0, new ProgressBarImage(217, 0, 17, 17, 234, 0, EnumFacing.DOWN, TextureResources.progressBars), this)); - modules.add(new ModuleButton(40, 56, 0, "Build", this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); + String buildLabel = LibVulpes.proxy.getLocalizedString("msg.rocketbuilder.build"); + modules.add(new ModuleButton(40, 56, 0, buildLabel, this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); modules.add(new ModuleButton(173, 3, 1, "", this, TextureResources.buttonCopy, LibVulpes.proxy.getLocalizedString("msg.satbuilder.writesecondchip"), 24, 24)); return modules; @@ -381,4 +382,4 @@ public void clear() { public boolean isEmpty() { return inventory.isEmpty(); } -} \ No newline at end of file +} diff --git a/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileSatelliteTerminal.java b/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileSatelliteTerminal.java index 7dcfaef7f..5d97635f4 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileSatelliteTerminal.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileSatelliteTerminal.java @@ -4,6 +4,8 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.EnumFacing; import net.minecraftforge.fml.relauncher.Side; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; @@ -15,12 +17,12 @@ import zmaster587.advancedRocketry.inventory.TextureResources; import zmaster587.advancedRocketry.inventory.modules.ModuleData; import zmaster587.advancedRocketry.inventory.modules.ModuleSatellite; -import zmaster587.advancedRocketry.item.ItemData; +import zmaster587.advancedRocketry.item.IDataItem; import zmaster587.advancedRocketry.item.ItemSatelliteIdentificationChip; -import zmaster587.advancedRocketry.network.PacketSatellite; import zmaster587.advancedRocketry.satellite.SatelliteData; import zmaster587.advancedRocketry.util.IDataInventory; import zmaster587.advancedRocketry.util.PlanetaryTravelHelper; + import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.inventory.modules.*; import zmaster587.libVulpes.network.PacketHandler; @@ -30,30 +32,128 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.LinkedList; + +import java.util.ArrayList; import java.util.List; +public class TileSatelliteTerminal extends TileInventoriedRFConsumer + implements INetworkMachine, IModularInventory, IButtonInventory, IDataInventory, IDataHandler { -public class TileSatelliteTerminal extends TileInventoriedRFConsumer implements INetworkMachine, IModularInventory, IButtonInventory, IDataInventory, IDataHandler { + private DataStorage data; + // Auto-download polling with exponential backoff + private static final int AUTO_DL_BASE_INTERVAL_TICKS = 64; // min interval + private static final int AUTO_DL_MAX_INTERVAL_TICKS = 512; // cap + private int autoDlInterval = AUTO_DL_BASE_INTERVAL_TICKS; // current interval + private long nextAutoDlTick = 0L; // worldTime gate - //private ModuleText satelliteText; - private SatelliteBase satellite; - private ModuleText moduleText; - private DataStorage data; public TileSatelliteTerminal() { super(10000, 2); - data = new DataStorage(); data.setMaxData(1000); } + private final BlockPos.MutableBlockPos mpos = new BlockPos.MutableBlockPos(); + + private boolean hasExtractPlugAdjacent() { + if (world == null) return false; + for (EnumFacing f : EnumFacing.values()) { + mpos.setPos(pos.getX() + f.getFrontOffsetX(), + pos.getY() + f.getFrontOffsetY(), + pos.getZ() + f.getFrontOffsetZ()); + if (!world.isBlockLoaded(mpos)) continue; + + TileEntity te = world.getTileEntity(mpos); + if (te instanceof zmaster587.advancedRocketry.tile.cables.TileWirelessTransciever) { + zmaster587.advancedRocketry.tile.cables.TileWirelessTransciever w = + (zmaster587.advancedRocketry.tile.cables.TileWirelessTransciever) te; + if (w.isEnabledWireless() && w.isExtractModeWireless()) return true; + } + } + return false; + } + + public final DataStorage getDataObject() { + return data; + } + + // Link+power check using an already-looked-up satellite + private boolean hasLinkAndPower(@Nonnull SatelliteBase sat) { + // must be a data satellite + if (!(sat instanceof zmaster587.advancedRocketry.satellite.SatelliteData)) return false; + + // check range + final int hereDim = zmaster587.advancedRocketry.dimension.DimensionManager + .getEffectiveDimId(world, pos).getId(); + final int satDim = sat.getDimensionId(); + + final boolean inRange = zmaster587.advancedRocketry.util.PlanetaryTravelHelper + .isTravelAnywhereInPlanetarySystem(satDim, hereDim); + if (!inRange) return false; + + // check power + return getUniversalEnergyStored() >= getPowerPerOperation(); + } + + // Keep convenience overload + private void maybeAutoDownloadFromSatellite() { + maybeAutoDownloadFromSatellite(false); + } + + private void maybeAutoDownloadFromSatellite(boolean force) { + if (world == null || world.isRemote) return; + + final long now = world.getTotalWorldTime(); + + if (force) { + autoDlInterval = AUTO_DL_BASE_INTERVAL_TICKS; + nextAutoDlTick = now + autoDlInterval; // avoid same-tick multi-pulls + } else if (now < nextAutoDlTick) { + return; + } + + // Buffer full → just schedule next check + if (data.getData() >= data.getMaxData()) { + nextAutoDlTick = now + autoDlInterval; + return; + } + + // No eligible plug → back off (unless forced) + if (!hasExtractPlugAdjacent()) { + if (!force) autoDlInterval = Math.min(autoDlInterval << 1, AUTO_DL_MAX_INTERVAL_TICKS); + nextAutoDlTick = now + autoDlInterval; + return; + } + + // Resolve satellite fresh from the chip each poll + SatelliteBase sat = resolveSatelliteFresh(); + if (sat == null) { + if (!force) autoDlInterval = Math.min(autoDlInterval << 1, AUTO_DL_MAX_INTERVAL_TICKS); + nextAutoDlTick = now + autoDlInterval; + return; + } + if (!hasLinkAndPower(sat)) { + if (!force) autoDlInterval = Math.min(autoDlInterval << 1, AUTO_DL_MAX_INTERVAL_TICKS); + nextAutoDlTick = now + autoDlInterval; + return; + } + + // Do the pull + sat.performAction(null, world, pos); + this.energy.extractEnergy(getPowerPerOperation(), false); + + // Success → reset interval + autoDlInterval = AUTO_DL_BASE_INTERVAL_TICKS; + nextAutoDlTick = now + autoDlInterval; + } + + + private static final int[] NO_SLOTS = new int[0]; + @Override @Nonnull - public int[] getSlotsForFace(@Nullable EnumFacing side) { - return new int[0]; - } + public int[] getSlotsForFace(@Nullable EnumFacing side) { return NO_SLOTS; } @Override public String getModularInventoryName() { @@ -62,240 +162,305 @@ public String getModularInventoryName() { @Override public boolean isItemValidForSlot(int slot, @Nonnull ItemStack stack) { - return true; + if (stack.isEmpty()) return true; + if (slot == 0) return stack.getItem() instanceof ItemSatelliteIdentificationChip; + if (slot == 1) return stack.getItem() instanceof IDataItem; + return false; } + + @Override public boolean canPerformFunction() { - return world.getTotalWorldTime() % 16 == 0 && getSatelliteFromSlot(0) != null; + if (world == null) return false; + final long now = world.getTotalWorldTime(); + return (now % 16 == 0) && (now >= nextAutoDlTick); } @Override - public int getPowerPerOperation() { - return 1; - } + public int getPowerPerOperation() { return 1; } @Override public void performFunction() { - if (world.isRemote) - updateInventoryInfo(); + if (world == null || world.isRemote) return; + maybeAutoDownloadFromSatellite(false); } - @Override - public void writeDataToNetwork(ByteBuf out, byte packetId) { - if (packetId == (byte) 22) { - satellite = getSatelliteFromSlot(0); - if (satellite != null && satellite instanceof SatelliteData) { - if (getUniversalEnergyStored() < getPowerPerOperation()) { - out.writeInt(1); // no power - } else { - if (!PlanetaryTravelHelper.isTravelAnywhereInPlanetarySystem(satellite.getDimensionId(), DimensionManager.getEffectiveDimId(world, pos).getId())) { - out.writeInt(2);//out of range - } else { - out.writeInt(3); - out.writeInt(((SatelliteData) satellite).getPowerPerTick()); - out.writeInt(((SatelliteData) satellite).data.getData()); - out.writeInt(((SatelliteData) satellite).data.getMaxData()); - } - } - } else { - out.writeInt(0); // no link - } - } - } + + // Old custom packet not used anymore; keep empty to satisfy INetworkMachine @Override - public void readDataFromNetwork(ByteBuf in, byte packetId, - NBTTagCompound nbt) { - if (packetId == (byte) 22) { - int status = in.readInt(); - if (status == 3){ - nbt.setInteger("ppt", in.readInt()); - nbt.setInteger("data", in.readInt()); - nbt.setInteger("maxdata", in.readInt()); - } - nbt.setInteger("status", status); - } - } + public void writeDataToNetwork(ByteBuf out, byte packetId) { } + // Old custom packet not used anymore; keep empty to satisfy INetworkMachine @Override - public void update() { - super.update(); + public void readDataFromNetwork(ByteBuf in, byte packetId, NBTTagCompound nbt) { } - if (!world.isRemote) { - //update satellite for players nearby - if ((world.getTotalWorldTime() % 20) == 0) { - PacketHandler.sendToNearby(new PacketMachine(this, (byte) 22), world.provider.getDimension(), pos, 16); - } - } - } + // Tick: nothing needed; the module polls the tile every 9 tick while GUI is open + //@Override + //public void update() { + // super.update(); + // no status pushing needed + //} @Override public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompound nbt) { - if (id == 0) { - storeData(0); + + if (id == -1) { + storeData(1); + return; + } else if (id == -2) { + loadData(1); + return; } else if (id == 100) { + // connect logic + if (!world.isRemote) { + SatelliteBase sat = getSatelliteFromSlot(0); + + boolean inRange = false; + if (sat != null) { + int satDim = sat.getDimensionId(); + int hereDim = DimensionManager.getEffectiveDimId(world, pos).getId(); + inRange = PlanetaryTravelHelper.isTravelAnywhereInPlanetarySystem(satDim, hereDim); + } - if (satellite != null && PlanetaryTravelHelper.isTravelAnywhereInPlanetarySystem(satellite.getDimensionId(), DimensionManager.getEffectiveDimId(world, pos).getId())) { - satellite.performAction(player, world, pos); + boolean hasLink = (sat instanceof SatelliteData) && inRange; + boolean hasPower = getUniversalEnergyStored() >= getPowerPerOperation(); + + if (hasLink && hasPower) { + sat.performAction(player, world, pos); + this.energy.extractEnergy(getPowerPerOperation(), false); + } } - } else if (id == 101) { - onInventoryButtonPressed(id - 100); - } + return; - if (id == 22) { - if (world.isRemote) { // 22 should never arrive at the server - int status = nbt.getInteger("status"); - satellite = getSatelliteFromSlot(0); - if (moduleText != null){ - if (status != 0 && satellite != null) { - if (status == 1) - moduleText.setText(LibVulpes.proxy.getLocalizedString("msg.notenoughpower")); - - else if (status == 2) { - moduleText.setText(satellite.getName() + "\n\n" + LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.toofar")); - } else if (status == 3) { - moduleText.setText(satellite.getName() + "\n\n" + LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.info") + "\n" + - "Power gen.: "+nbt.getInteger("ppt")+"\n"+ - "Data: "+nbt.getInteger("data") +"/"+nbt.getInteger("maxdata")); + } else if (id == 101) { + if (!world.isRemote) { + ItemStack stack = getStackInSlot(0); + if (!stack.isEmpty() && stack.getItem() instanceof ItemSatelliteIdentificationChip) { + ItemSatelliteIdentificationChip idchip = (ItemSatelliteIdentificationChip) stack.getItem(); + + SatelliteBase sat = idchip.getSatellite(stack); + if (sat != null) { + DimensionManager.getInstance() + .getDimensionProperties(sat.getDimensionId()) + .removeSatellite(sat.getId()); } - } else - moduleText.setText(LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.nolink")); + + idchip.erase(stack); + setInventorySlotContents(0, stack); } } + return; } } + + @Nullable + private SatelliteBase resolveSatelliteFresh() { + ItemStack s0 = getStackInSlot(0); + return (!s0.isEmpty() && s0.getItem() instanceof ItemSatelliteIdentificationChip) + ? ItemSatelliteIdentificationChip.getSatellite(s0) + : null; + } + @Override public void setInventorySlotContents(int slot, @Nonnull ItemStack stack) { super.setInventorySlotContents(slot, stack); - satellite = getSatelliteFromSlot(0); - updateInventoryInfo(); - } - - public void updateInventoryInfo() { - + if (!world.isRemote && slot == 0) { + maybeAutoDownloadFromSatellite(true); // force reset to base + } } - public SatelliteBase getSatelliteFromSlot(int slot) { - ItemStack stack = getStackInSlot(slot); if (!stack.isEmpty() && stack.getItem() instanceof ItemSatelliteIdentificationChip) { return ItemSatelliteIdentificationChip.getSatellite(stack); } - return null; } @Override public List getModules(int ID, EntityPlayer player) { + List modules = new ArrayList<>(6); - List modules = new LinkedList<>(); - modules.add(new ModulePower(18, 20, this.energy)); - modules.add(new ModuleButton(116, 70, 0, LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.connect"), this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild)); - modules.add(new ModuleButton(173, 3, 1, "", this, TextureResources.buttonKill, LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.destroysat"), 24, 24)); - modules.add(new ModuleData(28, 20, 1, this, data)); - ModuleSatellite moduleSatellite = new ModuleSatellite(152, 10, this, 0); - moduleSatellite.setSatellite(satellite); - modules.add(moduleSatellite); + modules.add(new ModulePower(18, 20, this.energy) { + @Override public int numberOfChangesToSend() { return 2; } + }); - //Try to assign a satellite ASAP - //moduleSatellite.setSatellite(getSatelliteFromSlot(0)); + modules.add(new ModuleButton( + 116, 70, 0, + LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.connect"), + this, + zmaster587.libVulpes.inventory.TextureResources.buttonBuild, + LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.autodl_hint") // tooltip + )); - moduleText = new ModuleText(60, 20, LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.nolink"), 0x404040); - modules.add(moduleText); - updateInventoryInfo(); + modules.add(new ModuleButton(173, 3, 1, "", + this, TextureResources.buttonKill, + LibVulpes.proxy.getLocalizedString("msg.satctrlcenter.destroysat"), 24, 24)); + + modules.add(new ModuleData(28, 20, 1, this, data) { + @Override public int numberOfChangesToSend() { return 2; } + }); + + modules.add(new ModuleSatellite(152, 10, this, 0) { + @Override public int numberOfChangesToSend() { return 0; } + }); + + // Add status module last; no need to keep a field reference + modules.add(new zmaster587.advancedRocketry.inventory.modules.ModuleSatelliteTerminal( + 60, 20, 0x404040, this, this)); + return modules; } + @Override public void onInventoryButtonPressed(int buttonId) { - if (buttonId == 0) { - PacketHandler.sendToServer(new PacketMachine(this, (byte) (100 + buttonId))); - + PacketHandler.sendToServer(new PacketMachine(this, (byte) (100 + buttonId))); // id 100 } else if (buttonId == 1) { - ItemStack stack = getStackInSlot(0); - - if (!stack.isEmpty() && stack.getItem() instanceof ItemSatelliteIdentificationChip) { - ItemSatelliteIdentificationChip idchip = (ItemSatelliteIdentificationChip) stack.getItem(); - - SatelliteBase satellite = idchip.getSatellite(stack); - - //Somebody might want to erase the chip of an already existing satellite - if (satellite != null) - DimensionManager.getInstance().getDimensionProperties(satellite.getDimensionId()).removeSatellite(satellite.getId()); - - idchip.erase(stack); - setInventorySlotContents(0, stack); - PacketHandler.sendToServer(new PacketMachine(this, (byte) (100 + buttonId))); - } + PacketHandler.sendToServer(new PacketMachine(this, (byte) (100 + buttonId))); // id 101 } - } @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); - - NBTTagCompound data = new NBTTagCompound(); - - this.data.writeToNBT(data); - nbt.setTag("data", data); + NBTTagCompound dataTag = new NBTTagCompound(); + this.data.writeToNBT(dataTag); + nbt.setTag("data", dataTag); return nbt; } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); + NBTTagCompound dataTag = nbt.getCompoundTag("data"); + this.data.readFromNBT(dataTag); + } - NBTTagCompound data = nbt.getCompoundTag("data"); - this.data.readFromNBT(data); + @Override + public int getInventoryStackLimit() { + return 1; } @Override - public void loadData(int id) { + public void loadData(int slotId) { + if (world == null || world.isRemote) { + // Client triggers server action + PacketHandler.sendToServer(new PacketMachine(this, (byte) -2)); + return; + } + + ItemStack stack = getStackInSlot(slotId); + if (stack.isEmpty() || !(stack.getItem() instanceof IDataItem)) return; + + IDataItem dataItem = (IDataItem) stack.getItem(); + DataStorage itemStore = dataItem.getDataStorage(stack); + + int available = itemStore.getData(); + if (available <= 0) return; + + DataType type = itemStore.getDataType(); + + // How much room does the terminal buffer have? + int room = data.getMaxData() - data.getData(); + if (room <= 0) return; + + int toMove = Math.min(available, room); + + // Add to terminal first (authoritative return value) + int added = data.addData(toMove, type, true); + + if (added > 0) { + // Remove only what actually got accepted + dataItem.removeData(stack, added, type); + setInventorySlotContents(slotId, stack); + markDirty(); + } } @Override - public void storeData(int id) { - if (!world.isRemote) { - ItemStack stack = getStackInSlot(1); - if (!stack.isEmpty() && stack.getItem() instanceof ItemData && stack.getCount() == 1) { - ItemData dataItem = (ItemData) stack.getItem(); - data.removeData(dataItem.addData(stack, data.getData(), data.getDataType()), true); - } - } else { - PacketHandler.sendToServer(new PacketMachine(this, (byte) 0)); + public void storeData(int slotId) { + if (world == null || world.isRemote) { + PacketHandler.sendToServer(new PacketMachine(this, (byte) -1)); + return; + } + + ItemStack stack = getStackInSlot(slotId); + if (stack.isEmpty() || !(stack.getItem() instanceof IDataItem)) return; + + if (data.getData() <= 0 || data.getDataType() == DataType.UNDEFINED) return; + + IDataItem dataItem = (IDataItem) stack.getItem(); + + int moved = dataItem.addData(stack, data.getData(), data.getDataType()); + + if (moved > 0) { + data.removeData(moved, true); + setInventorySlotContents(slotId, stack); + markDirty(); } } + + @Override public int extractData(int maxAmount, DataType type, EnumFacing dir, boolean commit) { - //TODO + // 1) Type guard + if (type != data.getDataType() && data.getDataType() != DataType.UNDEFINED) return 0; - SatelliteBase satellite = getSatelliteFromSlot(0); - if (satellite instanceof SatelliteData && PlanetaryTravelHelper.isTravelAnywhereInPlanetarySystem(satellite.getDimensionId(), DimensionManager.getEffectiveDimId(world, pos).getId())) { - satellite.performAction(null, world, pos); - } + // 2) Simulation + if (!commit) return Math.min(maxAmount, data.getData()); - if (type == data.getDataType() || data.getDataType() == DataType.UNDEFINED) { - return data.removeData(maxAmount, commit); - } + // 3) Drain LOCAL once + int toGive = Math.min(maxAmount, data.getData()); + int removed = (toGive > 0) ? data.removeData(toGive, true) : 0; - return 0; + // 4) Opportunistic refill (cheap guard inside function) + if (removed > 0) { + maybeAutoDownloadFromSatellite(true); + } + return removed; } + @Override public int addData(int maxAmount, DataType type, EnumFacing dir, boolean commit) { + int added = data.addData(maxAmount, type, commit); - return data.addData(maxAmount, type, commit); + return added; } @Override - public boolean canInteractWithContainer(EntityPlayer entity) { - return true; + public void onLoad() { + super.onLoad(); + if (!world.isRemote) { + // Reset backoff scheduler to a sane base state + autoDlInterval = AUTO_DL_BASE_INTERVAL_TICKS; + long now = world.getTotalWorldTime(); + // Warm-up so neighbors/registries settle (e.g. 80 ticks ~ 4s) + nextAutoDlTick = now + 80; + } } + + @Override + public void onChunkUnload() { + super.onChunkUnload(); + // Hard-clear any references and scheduling + autoDlInterval = AUTO_DL_BASE_INTERVAL_TICKS; + nextAutoDlTick = 0L; + } + + @Override + public void invalidate() { + super.invalidate(); + } + + + @Override + public boolean canInteractWithContainer(EntityPlayer entity) { return true; } } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileTerraformingTerminal.java b/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileTerraformingTerminal.java index 4a80d0de4..d508a37e6 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileTerraformingTerminal.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/satellite/TileTerraformingTerminal.java @@ -8,6 +8,8 @@ import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.world.biome.BiomeProvider; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.fml.relauncher.Side; import zmaster587.advancedRocketry.api.ARConfiguration; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; @@ -31,6 +33,7 @@ import zmaster587.libVulpes.network.PacketMachine; import zmaster587.libVulpes.tile.TileInventoriedRFConsumer; import zmaster587.libVulpes.util.INetworkMachine; +import zmaster587.libVulpes.cap.TeslaHandler; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -65,7 +68,7 @@ public int[] getSlotsForFace(@Nullable EnumFacing side) { @Override public String getModularInventoryName() { - return AdvancedRocketryBlocks.blockSatelliteControlCenter.getLocalizedName(); + return AdvancedRocketryBlocks.blockTerraformingTerminal.getLocalizedName(); } @Override @@ -124,30 +127,43 @@ public void setInventorySlotContents(int slot, @Nonnull ItemStack stack) { @Override public void update() { - super.update(); - boolean has_redstone = world.isBlockIndirectlyGettingPowered(getPos()) != 0; + + // Fast path: truly idle — no chip and never enabled + if (getStackInSlot(0).isEmpty() && !was_enabled_last_tick) { + return; + } + int powerrequired = 80; //120; + if (!world.isRemote) { + boolean has_redstone = world.isBlockIndirectlyGettingPowered(getPos()) != 0; + boolean has_valid = hasValidBiomeChanger(); - if ((world.getTotalWorldTime() + 6) % 21 == 0) - PacketHandler.sendToNearby(new PacketMachine(this, (byte) 22), world.provider.getDimension(), pos, 16); + // Only sync when there’s actually a biome changer present + if (has_valid && (world.getTotalWorldTime() + 6) % 21 == 0) { + PacketHandler.sendToNearby(new PacketMachine(this, (byte) 22), + world.provider.getDimension(), pos, 16); + } - if (hasValidBiomeChanger() && has_redstone) { + if (has_valid && has_redstone) { was_enabled_last_tick = true; - if(!world.getBlockState(pos).getValue(BlockTileTerraformer.STATE)){ - world.setBlockState(pos, world.getBlockState(pos).withProperty(BlockTileTerraformer.STATE, true), 3); + if (!world.getBlockState(pos).getValue(BlockTileTerraformer.STATE)) { + world.setBlockState(pos, + world.getBlockState(pos).withProperty(BlockTileTerraformer.STATE, true), 3); } Item biomeChanger = getStackInSlot(0).getItem(); if (biomeChanger instanceof ItemBiomeChanger) { - SatelliteBiomeChanger sat = (SatelliteBiomeChanger) ItemSatelliteIdentificationChip.getSatellite(getStackInSlot(0)); + SatelliteBiomeChanger sat = (SatelliteBiomeChanger) + ItemSatelliteIdentificationChip.getSatellite(getStackInSlot(0)); sat_power_per_tick = sat.getPowerPerTick(); randomblocks_per_tick = (float) sat_power_per_tick / powerrequired; } } else { was_enabled_last_tick = false; - if(world.getBlockState(pos).getValue(BlockTileTerraformer.STATE)){ - world.setBlockState(pos, world.getBlockState(pos).withProperty(BlockTileTerraformer.STATE, false), 3); + if (world.getBlockState(pos).getValue(BlockTileTerraformer.STATE)) { + world.setBlockState(pos, + world.getBlockState(pos).withProperty(BlockTileTerraformer.STATE, false), 3); } } } @@ -204,24 +220,46 @@ public void update() { public void updateInventoryInfo() { if (moduleText != null) { - if (hasValidBiomeChanger() && world.isBlockIndirectlyGettingPowered(getPos()) != 0) { BigDecimal bd = new BigDecimal(randomblocks_per_tick); bd = bd.setScale(2, RoundingMode.HALF_UP); - moduleText.setText("terraforming planet...\n" + - "\nPower generation:" + sat_power_per_tick + - "\nBlocks per tick:" + bd); + String header = LibVulpes.proxy.getLocalizedString("msg.terraformingterminal.terraforming"); + String powerLabel = LibVulpes.proxy.getLocalizedString("msg.terraformingterminal.powergen"); + String blocksLabel = LibVulpes.proxy.getLocalizedString("msg.terraformingterminal.blockspertick"); + + moduleText.setText( + header + "\n" + + "\n" + powerLabel + " " + sat_power_per_tick + + "\n" + blocksLabel + " " + bd + ); } else if (hasValidBiomeChanger()) { - moduleText.setText("provide redstone signal\nto start the process"); + + String redstoneLine1 = LibVulpes.proxy.getLocalizedString("msg.terraformingterminal.needredstone.line1"); + String redstoneLine2 = LibVulpes.proxy.getLocalizedString("msg.terraformingterminal.needredstone.line2"); + + moduleText.setText( + redstoneLine1 + "\n" + + redstoneLine2 + ); + } else { - moduleText.setText("place a biome remote here\nto make the satellite terraform\nthe entire planet"); - } + String insertLine1 = LibVulpes.proxy.getLocalizedString("msg.terraformingterminal.insertchip.line1"); + String insertLine2 = LibVulpes.proxy.getLocalizedString("msg.terraformingterminal.insertchip.line2"); + String insertLine3 = LibVulpes.proxy.getLocalizedString("msg.terraformingterminal.insertchip.line3"); + + moduleText.setText( + "\n" + insertLine1 + "\n" + + insertLine2 + "\n" + + insertLine3 + ); + } } } + public boolean hasValidBiomeChanger() { ItemStack biomeChanger = getStackInSlot(0); SatelliteBase satellite; @@ -265,6 +303,45 @@ public NBTTagCompound writeToNBT(NBTTagCompound nbt) { return nbt; } + @Override + public boolean canConnectEnergy(EnumFacing side) { + return false; + } + + @Override + public boolean canReceive() { + return false; + } + + @Override + public int getEnergyStored(EnumFacing side) { + return 0; + } + + @Override + public int getMaxEnergyStored(EnumFacing side) { + return 0; + } + + @Override + public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) { + // Hide Forge Energy capability + if (capability == CapabilityEnergy.ENERGY) return false; + // Hide any Tesla capability the base class would expose + if (TeslaHandler.hasTeslaCapability(this, capability)) return false; + return super.hasCapability(capability, facing); + } + + @Override + @Nullable + public T getCapability(Capability capability, @Nullable EnumFacing facing) { + // Don’t provide energy handlers to probes/pipes + if (capability == CapabilityEnergy.ENERGY) return null; + if (TeslaHandler.hasTeslaCapability(this, capability)) return null; + return super.getCapability(capability, facing); + } + + @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); diff --git a/src/main/java/zmaster587/advancedRocketry/tile/station/TileLandingPad.java b/src/main/java/zmaster587/advancedRocketry/tile/station/TileLandingPad.java index 5e1d5c20a..2242ad73a 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/station/TileLandingPad.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/station/TileLandingPad.java @@ -179,22 +179,32 @@ public void onRocketLaunch(RocketPreLaunchEvent event) { @SubscribeEvent public void onRocketDismantle(RocketDismantleEvent event) { - if (!world.isRemote && world.provider.getDimension() == ARConfiguration.getCurrentConfig().spaceDimId) { + if (world == null || world.isRemote || world.provider == null) return; + if (world.provider.getDimension() != ARConfiguration.getCurrentConfig().spaceDimId) return; - EntityRocketBase rocket = (EntityRocketBase) event.getEntity(); - AxisAlignedBB bbCache = new AxisAlignedBB(this.getPos().add(-1, 0, -1), this.getPos().add(1, 2, 1)); + // Make sure this is actually a rocket + if (!(event.getEntity() instanceof EntityRocketBase)) return; - if (bbCache.intersects(rocket.getEntityBoundingBox())) { + EntityRocketBase rocket = (EntityRocketBase) event.getEntity(); + AxisAlignedBB rocketBB = rocket.getEntityBoundingBox(); + if (rocketBB == null) return; // don't explode if some mod messes with AABBs - ISpaceObject spaceObj = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + AxisAlignedBB bbCache = new AxisAlignedBB( + this.getPos().add(-1, 0, -1), + this.getPos().add(1, 2, 1) + ); - if (spaceObj instanceof SpaceStationObject) { - ((SpaceStationObject) spaceObj).setPadStatus(pos, false); - } - } + if (!bbCache.intersects(rocketBB)) return; + + ISpaceObject spaceObj = + SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + + if (spaceObj instanceof SpaceStationObject) { + ((SpaceStationObject) spaceObj).setPadStatus(pos, false); } } + public void registerTileWithStation(World world, BlockPos pos) { if (!world.isRemote && world.provider.getDimension() == ARConfiguration.getCurrentConfig().spaceDimId) { ISpaceObject spaceObj = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); diff --git a/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationAltitudeController.java b/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationAltitudeController.java index 6ed4760ab..eda5fadc3 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationAltitudeController.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationAltitudeController.java @@ -1,6 +1,8 @@ package zmaster587.advancedRocketry.tile.station; import io.netty.buffer.ByteBuf; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; @@ -11,6 +13,7 @@ import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; import zmaster587.advancedRocketry.api.stations.ISpaceObject; import zmaster587.advancedRocketry.inventory.TextureResources; +import zmaster587.advancedRocketry.network.PacketSpaceStationInfo; import zmaster587.advancedRocketry.network.PacketStationUpdate; import zmaster587.advancedRocketry.stations.SpaceObjectManager; import zmaster587.advancedRocketry.stations.SpaceStationObject; @@ -30,12 +33,14 @@ public class TileStationAltitudeController extends TileEntity implements IModula int progress; private RedstoneState state = RedstoneState.OFF; - + private long lastAltSyncTick = -10; private ModuleText moduleGrav, numGravPylons, maxGravBuildSpeed, targetGrav; private ModuleRedstoneOutputButton redstoneControl; - + private boolean wasChanging = false; + private ModuleText anchoredWarning; public TileStationAltitudeController() { moduleGrav = new ModuleText(6, 15, "Altitude: ", 0xaa2020); + anchoredWarning = new ModuleText(6, 45, "", 0xaa2020); //numGravPylons = new ModuleText(10, 25, "Number Of Thrusters: ", 0xaa2020); //maxGravBuildSpeed = new ModuleText(6, 25, LibVulpes.proxy.getLocalizedString("msg.stationaltctrl.maxaltrate"), 0xaa2020); targetGrav = new ModuleText(6, 35, LibVulpes.proxy.getLocalizedString("msg.stationaltctrl.tgtalt"), 0x202020); @@ -46,15 +51,86 @@ public TileStationAltitudeController() { @Override public List getModules(int id, EntityPlayer player) { List modules = new LinkedList<>(); + if (!world.isRemote) { + ISpaceObject so = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (so instanceof SpaceStationObject) { + PacketHandler.sendToPlayer(new PacketSpaceStationInfo(so.getId(), (SpaceStationObject) so), player); + } + } modules.add(moduleGrav); //modules.add(numThrusters); //modules.add(maxGravBuildSpeed); - modules.add(targetGrav); + modules.add(anchoredWarning); modules.add(new ModuleSlider(6, 60, 0, TextureResources.doubleWarningSideBarIndicator, this)); modules.add(redstoneControl); - updateText(); + // inline updater that runs only while GUI is open + modules.add(new ModuleBase(0, 0) { + private SpaceStationObject cached; + private int cachedId = Integer.MIN_VALUE; + + // last shown keys (ints in Km) + private int lastAltKm = Integer.MIN_VALUE; + private int lastTgtKm = Integer.MIN_VALUE; + private int lastAnchored = Integer.MIN_VALUE; + + // localized prefixes (cache per GUI session) + private final String anchoredText = LibVulpes.proxy.getLocalizedString("msg.station.anchored"); + private final String prefixAlt = LibVulpes.proxy.getLocalizedString("msg.stationaltctrl.alt"); + private final String prefixTgt = LibVulpes.proxy.getLocalizedString("msg.stationaltctrl.tgtalt"); + + private boolean ensureStation() { + if (cached == null) { + ISpaceObject so = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (!(so instanceof SpaceStationObject)) return false; + cached = (SpaceStationObject) so; + cachedId = so.getId(); + return true; + } + ISpaceObject current = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (!(current instanceof SpaceStationObject)) { cached = null; cachedId = Integer.MIN_VALUE; return false; } + if (current.getId() != cachedId) { + cached = (SpaceStationObject) current; + cachedId = current.getId(); + } + return true; + } + + @Override + public void renderBackground(GuiContainer gui, int x, int y, int mouseX, int mouseY, FontRenderer font) { + if (!ensureStation()) return; + + // Anchored status + boolean anchored = cached.isAnchored(); + int anchoredKey = anchored ? 1 : 0; + if (anchoredKey != lastAnchored) { + anchoredWarning.setText(anchored ? anchoredText : ""); + lastAnchored = anchoredKey; + } + + // Compute display keys (Km as ints) + int curAltKm = (int)Math.round(cached.getOrbitalDistance() * 200.0 + 100.0); + int tgtAltKm = cached.targetOrbitalDistance * 200 + 100; + + // Update only when the visible value changes + if (curAltKm != lastAltKm) { + // "Altitude: XXXKm" + moduleGrav.setText(prefixAlt + " " + curAltKm + "Km"); + lastAltKm = curAltKm; + } + if (tgtAltKm != lastTgtKm) { + // "Target Altitude: YYY" + targetGrav.setText(prefixTgt + " " + tgtAltKm); + lastTgtKm = tgtAltKm; + } + } + + @Override public int getSizeX() { return 0; } + @Override public int getSizeY() { return 0; } + }); + + return modules; } @@ -83,62 +159,50 @@ public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { readFromNBT(pkt.getNbtCompound()); } - private void updateText() { - if (world.isRemote) { - ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); - if (spaceObject != null) { - moduleGrav.setText(String.format("%s %.0fKm", LibVulpes.proxy.getLocalizedString("msg.stationaltctrl.alt"), spaceObject.getOrbitalDistance() * 200 + 100)); - //maxGravBuildSpeed.setText(String.format("%s%.1f", LibVulpes.proxy.getLocalizedString("msg.stationaltctrl.maxaltrate"), 7200D * spaceObject.getMaxRotationalAcceleration())); - targetGrav.setText(String.format("%s %d", LibVulpes.proxy.getLocalizedString("msg.stationaltctrl.tgtalt"), ((SpaceStationObject) spaceObject).targetOrbitalDistance * 200 + 100)); - } + @Override + public void update() { + if (!(world.provider instanceof WorldProviderSpace) || world.isRemote) return; - //numThrusters.setText("Number Of Thrusters: 0"); + ISpaceObject so = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (so == null) return; + SpaceStationObject sso = (SpaceStationObject) so; + + // Redstone → target + if (redstoneControl.getState() == RedstoneState.ON) { + sso.targetOrbitalDistance = Math.max((world.getStrongPower(pos) * 13) + 4, 190); + } else if (redstoneControl.getState() == RedstoneState.INVERTED) { + sso.targetOrbitalDistance = Math.max(Math.abs(15 - world.getStrongPower(pos)) * 13 + 4, 190); } - } - @Override - public void update() { - if (this.world.provider instanceof WorldProviderSpace) { + progress = sso.targetOrbitalDistance; - if (!world.isRemote) { - ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + // Converge with epsilon + double target = sso.targetOrbitalDistance; + double current = so.getOrbitalDistance(); + double diff = target - current; + double acc = 0.02D; + boolean changing = Math.abs(diff) >= 0.001D; - if (spaceObject != null) { - if (redstoneControl.getState() == RedstoneState.ON) - ((SpaceStationObject) spaceObject).targetOrbitalDistance = Math.max((world.getStrongPower(pos) * 13) + 4, 190); - else if (redstoneControl.getState() == RedstoneState.INVERTED) - ((SpaceStationObject) spaceObject).targetOrbitalDistance = Math.max(Math.abs(15 - world.getStrongPower(pos)) * 13 + 4, 190); - - progress = ((SpaceStationObject) spaceObject).targetOrbitalDistance; - - double targetGravity = ((SpaceStationObject) spaceObject).targetOrbitalDistance; - double angVel = spaceObject.getOrbitalDistance(); - double acc = 0.02;//0.1 * (getTotalProgress(0) - angVel + 1) / (float) getTotalProgress(0); - - double difference = targetGravity - angVel; - - if (difference != 0) { - double finalVel = angVel; - if (difference < 0) { - finalVel = angVel + Math.max(difference, -acc); - } else if (difference > 0) { - finalVel = angVel + Math.min(difference, acc); - } - - spaceObject.setOrbitalDistance((float) finalVel); - if (!world.isRemote) { - //PacketHandler.sendToNearby(new PacketStationUpdate(spaceObject, PacketStationUpdate.Type.ROTANGLE_UPDATE), this.worldObj.provider.dimensionId, this.xCoord, this.yCoord, this.zCoord, 1024); - PacketHandler.sendToAll(new PacketStationUpdate(spaceObject, PacketStationUpdate.Type.ALTITUDE_UPDATE)); - markDirty(); - } else - updateText(); - } - } - } else - updateText(); + if (changing) { + double next = current + (diff < 0 ? Math.max(diff, -acc) : Math.min(diff, acc)); + so.setOrbitalDistance((float) next); + + long wt = world.getTotalWorldTime(); + if (wt - lastAltSyncTick >= 10L) { // ~4 Hz while changing + PacketHandler.sendToAll(new PacketStationUpdate(so, PacketStationUpdate.Type.ALTITUDE_UPDATE)); + lastAltSyncTick = wt; + } + markDirty(); + } else if (wasChanging) { + // final flush on settle so clients end exactly on the last value + PacketHandler.sendToAll(new PacketStationUpdate(so, PacketStationUpdate.Type.ALTITUDE_UPDATE)); + markDirty(); } + + wasChanging = changing; } + @Override public String getModularInventoryName() { return AdvancedRocketryBlocks.blockAltitudeController.getLocalizedName(); diff --git a/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationGravityController.java b/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationGravityController.java index 51446c235..34d5e4bcb 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationGravityController.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationGravityController.java @@ -1,6 +1,8 @@ package zmaster587.advancedRocketry.tile.station; import io.netty.buffer.ByteBuf; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; @@ -34,6 +36,7 @@ public class TileStationGravityController extends TileEntity implements IModular private RedstoneState state = RedstoneState.OFF; private ModuleText moduleGrav, maxGravBuildSpeed, targetGrav; private ModuleRedstoneOutputButton redstoneControl; + private long lastDimPropSyncTick = -5; public TileStationGravityController() { moduleGrav = new ModuleText(6, 15, LibVulpes.proxy.getLocalizedString("msg.stationgravctrl.alt"), 0xaa2020); @@ -54,14 +57,95 @@ public static int getMinGravity() { public List getModules(int id, EntityPlayer player) { List modules = new LinkedList<>(); modules.add(moduleGrav); - //modules.add(numThrusters); modules.add(maxGravBuildSpeed); modules.add(redstoneControl); modules.add(targetGrav); modules.add(new ModuleSlider(6, 60, 0, TextureResources.doubleWarningSideBarIndicator, this)); - updateText(); + // inline updater that runs only while GUI is open + modules.add(new ModuleBase(0, 0) { + // --- Caches (live only for GUI lifetime) --- + private SpaceStationObject cached; // strong ref during GUI life + private int cachedId = Integer.MIN_VALUE; // station id for validation + + // last *displayed* keys (so we only update text when the user can see a change) + private int lastGravKey = Integer.MIN_VALUE; // 2dp: round(grav*100) + private int lastRateKey = Integer.MIN_VALUE; // 1dp: round(rate*10) + private int lastTgtKey = Integer.MIN_VALUE; // int + + // localized prefixes + private final String prefixGrav = LibVulpes.proxy.getLocalizedString("msg.stationgravctrl.alt"); + private final String prefixMax = LibVulpes.proxy.getLocalizedString("msg.stationgravctrl.maxaltrate"); + private final String prefixTgt = LibVulpes.proxy.getLocalizedString("msg.stationgravctrl.tgtalt"); + + // tiny formatters (no String.format churn) --- + private String twoDpFromKey(int key) { // key = round(value * 100) + int abs = Math.abs(key), whole = abs / 100, frac = abs % 100; + String s = whole + "." + (frac < 10 ? "0" : "") + frac; + return key < 0 ? "-" + s : s; + } + private String oneDpFromKey(int key) { // key = round(value * 10) + int abs = Math.abs(key), whole = abs / 10, frac = abs % 10; + String s = whole + "." + frac; + return key < 0 ? "-" + s : s; + } + + // Resolve or revalidate the cached station safely. + private boolean ensureStation() { + // Resolve if no cache yet + if (cached == null) { + ISpaceObject so = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (!(so instanceof SpaceStationObject)) return false; + cached = (SpaceStationObject) so; + cachedId = so.getId(); + return true; + } + // Revalidate in case manager swapped instances + ISpaceObject current = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (!(current instanceof SpaceStationObject)) { cached = null; cachedId = Integer.MIN_VALUE; return false; } + if (current.getId() != cachedId) { // instance swapped or different station under pos + cached = (SpaceStationObject) current; + cachedId = current.getId(); + } + return true; + } + + @Override + public void renderBackground(GuiContainer gui, int x, int y, int mouseX, int mouseY, FontRenderer font) { + // Only runs while GUI is visible → zero idle cost when closed. + if (!ensureStation()) return; + + // Pull current (client-synced) values + float grav = cached.getProperties().getGravitationalMultiplier(); // e.g. 0.57 + double maxRate = 7200D * cached.getMaxRotationalAcceleration(); // e.g. 144.0 + int tgt = cached.targetGravity; // 10..100 + + // Compute compare keys at display precision + int gravKey = Math.round(grav * 100f); // 2dp + int rateKey = (int)Math.round(maxRate * 10d); // 1dp + int tgtKey = tgt; // int + + // Only touch ModuleText when the visible value actually changes + if (gravKey != lastGravKey) { + moduleGrav.setText(prefixGrav + twoDpFromKey(gravKey)); + lastGravKey = gravKey; + } + if (rateKey != lastRateKey) { + maxGravBuildSpeed.setText(prefixMax + oneDpFromKey(rateKey)); + lastRateKey = rateKey; + } + if (tgtKey != lastTgtKey) { + targetGrav.setText(prefixTgt + tgtKey); + lastTgtKey = tgtKey; + } + } + + @Override public int getSizeX() { return 0; } // no visual footprint + @Override public int getSizeY() { return 0; } + }); + + return modules; } @@ -103,49 +187,46 @@ private void updateText() { @Override public void update() { + if (!(this.world.provider instanceof WorldProviderSpace)) return; - if (this.world.provider instanceof WorldProviderSpace) { + if (!world.isRemote) { + ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (spaceObject == null) return; - if (!world.isRemote) { - ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (redstoneControl.getState() == RedstoneState.ON) { + ((SpaceStationObject) spaceObject).targetGravity = (world.getStrongPower(pos) * 6) + 10; + } else if (redstoneControl.getState() == RedstoneState.INVERTED) { + ((SpaceStationObject) spaceObject).targetGravity = Math.abs(15 - world.getStrongPower(pos)) * 6 + 10; + } - if (spaceObject != null) { - if (redstoneControl.getState() == RedstoneState.ON) - ((SpaceStationObject) spaceObject).targetGravity = (world.getStrongPower(pos) * 6) + 10; - else if (redstoneControl.getState() == RedstoneState.INVERTED) - ((SpaceStationObject) spaceObject).targetGravity = Math.abs(15 - world.getStrongPower(pos)) * 6 + 10; - - progress = ((SpaceStationObject) spaceObject).targetGravity - minGravity; - - int targetMultiplier = (ARConfiguration.getCurrentConfig().allowZeroGSpacestations) ? ((SpaceStationObject) spaceObject).targetGravity : Math.max(10, ((SpaceStationObject) spaceObject).targetGravity); - double targetGravity = targetMultiplier / 100D; - double angVel = spaceObject.getProperties().getGravitationalMultiplier(); - double acc = 0.001; - - double difference = targetGravity - angVel; - - if (Math.abs(difference) >= 0.001) { - double finalVel = angVel; - if (difference < 0) { - finalVel = angVel + Math.max(difference, -acc); - } else if (difference > 0) { - finalVel = angVel + Math.min(difference, acc); - } - - spaceObject.getProperties().setGravitationalMultiplier((float) finalVel); - if (!world.isRemote) { - //PacketHandler.sendToNearby(new PacketStationUpdate(spaceObject, PacketStationUpdate.Type.ROTANGLE_UPDATE), this.worldObj.provider.dimensionId, this.xCoord, this.yCoord, this.zCoord, 1024); - PacketHandler.sendToAll(new PacketStationUpdate(spaceObject, PacketStationUpdate.Type.DIM_PROPERTY_UPDATE)); - markDirty(); - } else - updateText(); - } + progress = ((SpaceStationObject) spaceObject).targetGravity - minGravity; + + int targetMultiplier = ARConfiguration.getCurrentConfig().allowZeroGSpacestations + ? ((SpaceStationObject) spaceObject).targetGravity + : Math.max(10, ((SpaceStationObject) spaceObject).targetGravity); + + double targetGravity = targetMultiplier / 100D; + double angVel = spaceObject.getProperties().getGravitationalMultiplier(); + double acc = 0.001; + + double difference = targetGravity - angVel; + if (Math.abs(difference) >= 0.001) { + double finalVel = angVel + (difference < 0 ? Math.max(difference, -acc) : Math.min(difference, acc)); + spaceObject.getProperties().setGravitationalMultiplier((float) finalVel); + + long wt = world.getTotalWorldTime(); + + if ((wt - lastDimPropSyncTick) >= 5) { // every 5 ticks ≈ 4 Hz + PacketHandler.sendToAll(new PacketStationUpdate(spaceObject, PacketStationUpdate.Type.DIM_PROPERTY_UPDATE)); + lastDimPropSyncTick = wt; } - } else - updateText(); + + markDirty(); + } } } + @Override public String getModularInventoryName() { return AdvancedRocketryBlocks.blockGravityController.getLocalizedName(); diff --git a/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationOrientationController.java b/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationOrientationController.java index d3ed36055..9bce3d6e7 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationOrientationController.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/station/TileStationOrientationController.java @@ -1,6 +1,8 @@ package zmaster587.advancedRocketry.tile.station; import io.netty.buffer.ByteBuf; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; @@ -10,6 +12,7 @@ import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; import zmaster587.advancedRocketry.api.stations.ISpaceObject; import zmaster587.advancedRocketry.inventory.TextureResources; +import zmaster587.advancedRocketry.network.PacketSpaceStationInfo; import zmaster587.advancedRocketry.network.PacketStationUpdate; import zmaster587.advancedRocketry.stations.SpaceObjectManager; import zmaster587.advancedRocketry.stations.SpaceStationObject; @@ -26,13 +29,15 @@ public class TileStationOrientationController extends TileEntity implements ITickable, IModularInventory, INetworkMachine, ISliderBar, IButtonInventory { private int[] progress; - + private long lastRotSyncTick = -5; + private ModuleText anchoredWarning; private ModuleText moduleAngularVelocity, numThrusters, maxAngularAcceleration, targetRotations; public TileStationOrientationController() { moduleAngularVelocity = new ModuleText(6, 15, LibVulpes.proxy.getLocalizedString("msg.stationorientctrl.alt"), 0xaa2020); //numThrusters = new ModuleText(10, 25, "Number Of Thrusters: ", 0xaa2020); targetRotations = new ModuleText(6, 25, LibVulpes.proxy.getLocalizedString("msg.stationorientctrl.tgtalt"), 0x202020); + anchoredWarning = new ModuleText(6, 35, "", 0xaa2020); progress = new int[3]; progress[0] = getTotalProgress(0) / 2; @@ -43,83 +48,139 @@ public TileStationOrientationController() { @Override public List getModules(int id, EntityPlayer player) { List modules = new LinkedList<>(); + if (!world.isRemote) { + ISpaceObject so = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (so instanceof SpaceStationObject) { + PacketHandler.sendToPlayer(new PacketSpaceStationInfo(so.getId(), (SpaceStationObject) so), player); + } + } modules.add(moduleAngularVelocity); //modules.add(numThrusters); //modules.add(maxAngularAcceleration); modules.add(targetRotations); - + modules.add(anchoredWarning); + modules.add(new ModuleText(10, 54, "X:", 0x202020)); modules.add(new ModuleText(10, 69, "Y:", 0x202020)); //AYYYY modules.add(new ModuleSlider(24, 50, 0, TextureResources.doubleWarningSideBarIndicator, this)); modules.add(new ModuleSlider(24, 65, 1, TextureResources.doubleWarningSideBarIndicator, this)); - modules.add(new ModuleButton(25, 35, 2, LibVulpes.proxy.getLocalizedString("msg.spacelaser.reset"), this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild, 36, 15)); + modules.add(new ModuleButton(128, 35, 2, LibVulpes.proxy.getLocalizedString("msg.spacelaser.reset"), this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild, 36, 15)); //modules.add(new ModuleSlider(24, 35, 2, TextureResources.doubleWarningSideBarIndicator, (ISliderBar)this)); - updateText(); - return modules; - } + // inline updater that runs only while GUI is open + modules.add(new ModuleBase(0, 0) { + private SpaceStationObject cached; + private int cachedId = Integer.MIN_VALUE; + + private int lastVelX = Integer.MIN_VALUE, lastVelY = Integer.MIN_VALUE, lastVelZ = Integer.MIN_VALUE; + private int lastTgtX = Integer.MIN_VALUE, lastTgtY = Integer.MIN_VALUE, lastTgtZ = Integer.MIN_VALUE; + private int lastAnchored = Integer.MIN_VALUE; + private final String anchoredText = LibVulpes.proxy.getLocalizedString("msg.station.anchored"); + private final String prefixVel = LibVulpes.proxy.getLocalizedString("msg.stationorientctrl.alt"); + private final String prefixTgt = LibVulpes.proxy.getLocalizedString("msg.stationorientctrl.tgtalt"); + + private String oneDp(int key) { + int abs = Math.abs(key), whole = abs / 10, frac = abs % 10; + String s = whole + "." + frac; + return key < 0 ? "-" + s : s; + } - private void updateText() { - if (world.isRemote) { - ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); - if (spaceObject != null) { - moduleAngularVelocity.setText(String.format("%s%.1f %.1f %.1f", LibVulpes.proxy.getLocalizedString("msg.stationorientctrl.alt"), 72000D * spaceObject.getDeltaRotation(EnumFacing.EAST), 72000D * spaceObject.getDeltaRotation(EnumFacing.UP), 7200D * spaceObject.getDeltaRotation(EnumFacing.NORTH))); - //maxAngularAcceleration.setText(String.format("Maximum Angular Acceleration: %.1f", 7200D*object.getMaxRotationalAcceleration())); + private boolean ensureStation() { + if (cached == null) { + ISpaceObject so = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (!(so instanceof SpaceStationObject)) return false; + cached = (SpaceStationObject) so; + cachedId = so.getId(); + return true; + } + ISpaceObject current = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (!(current instanceof SpaceStationObject)) { cached = null; cachedId = Integer.MIN_VALUE; return false; } + if (current.getId() != cachedId) { + cached = (SpaceStationObject) current; + cachedId = current.getId(); + } + return true; + } - //numThrusters.setText("Number Of Thrusters: 0"); - int[] targetRotationsPerHour = ((SpaceStationObject) spaceObject).targetRotationsPerHour; - targetRotations.setText(String.format("%s%d %d %d", LibVulpes.proxy.getLocalizedString("msg.stationorientctrl.tgtalt"), targetRotationsPerHour[0], targetRotationsPerHour[1], targetRotationsPerHour[2])); + @Override + public void renderBackground(GuiContainer gui, int x, int y, int mouseX, int mouseY, FontRenderer font) { + if (!ensureStation()) return; + + boolean anchored = cached.isAnchored(); + int anchoredKey = anchored ? 1 : 0; + if (anchoredKey != lastAnchored) { + anchoredWarning.setText(anchored ? anchoredText : ""); + lastAnchored = anchoredKey; + } + + double dX = cached.getDeltaRotation(EnumFacing.EAST); + double dY = cached.getDeltaRotation(EnumFacing.UP); + double dZ = cached.getDeltaRotation(EnumFacing.NORTH); + int[] tgt = cached.targetRotationsPerHour; + + int velX = (int)Math.round(72000D * dX * 10D); + int velY = (int)Math.round(72000D * dY * 10D); + int velZ = (int)Math.round( 7200D * dZ * 10D); + + if (velX != lastVelX || velY != lastVelY || velZ != lastVelZ) { + moduleAngularVelocity.setText(prefixVel + oneDp(velX) + " " + oneDp(velY) + " " + oneDp(velZ)); + lastVelX = velX; lastVelY = velY; lastVelZ = velZ; + } + + if (tgt[0] != lastTgtX || tgt[1] != lastTgtY || tgt[2] != lastTgtZ) { + targetRotations.setText(prefixTgt + tgt[0] + " " + tgt[1] + " " + tgt[2]); + lastTgtX = tgt[0]; lastTgtY = tgt[1]; lastTgtZ = tgt[2]; + } } - } + + @Override public int getSizeX() { return 0; } + @Override public int getSizeY() { return 0; } + }); + + + return modules; } @Override public void update() { + // Only relevant in space + if (!(world.provider instanceof WorldProviderSpace)) return; + // Server-side only + if (world.isRemote) return; - if (this.world.provider instanceof WorldProviderSpace) { - if (!world.isRemote) { - ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); - boolean update = false; - - if (spaceObject != null) { - - EnumFacing[] dirs = {EnumFacing.EAST, EnumFacing.UP, EnumFacing.NORTH}; - int[] targetRotationsPerHour = ((SpaceStationObject) spaceObject).targetRotationsPerHour; - for (int i = 0; i < 3; i++) { - setProgress(i, targetRotationsPerHour[i] + (getTotalProgress(i) / 2)); - } - - - for (int i = 0; i < 3; i++) { - - double targetAngularVelocity = targetRotationsPerHour[i] / 72000D; - double angVel = spaceObject.getDeltaRotation(dirs[i]); - double acc = spaceObject.getMaxRotationalAcceleration(); - - double difference = targetAngularVelocity - angVel; - - if (difference != 0) { - double finalVel = angVel; - if (difference < 0) { - finalVel = angVel + Math.max(difference, -acc); - } else if (difference > 0) { - finalVel = angVel + Math.min(difference, acc); - } - - spaceObject.setDeltaRotation(finalVel, dirs[i]); - update = true; - } - } - - if (!world.isRemote && update) { - //PacketHandler.sendToNearby(new PacketStationUpdate(spaceObject, PacketStationUpdate.Type.ROTANGLE_UPDATE), this.worldObj.provider.dimensionId, this.xCoord, this.yCoord, this.zCoord, 1024); - PacketHandler.sendToAll(new PacketStationUpdate(spaceObject, PacketStationUpdate.Type.ROTANGLE_UPDATE)); - } - } else - updateText(); - } else - updateText(); + ISpaceObject spaceObject = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(pos); + if (spaceObject == null) return; + + EnumFacing[] dirs = { EnumFacing.EAST, EnumFacing.UP, EnumFacing.NORTH }; + int[] targetRotationsPerHour = ((SpaceStationObject) spaceObject).targetRotationsPerHour; + + // keep sliders in sync with server state + for (int i = 0; i < 3; i++) { + setProgress(i, targetRotationsPerHour[i] + (getTotalProgress(i) / 2)); + } + + boolean updated = false; + + for (int i = 0; i < 3; i++) { + double targetAngularVelocity = targetRotationsPerHour[i] / 72000D; + double angVel = spaceObject.getDeltaRotation(dirs[i]); + double acc = spaceObject.getMaxRotationalAcceleration(); + + double difference = targetAngularVelocity - angVel; + if (difference != 0) { + double finalVel = angVel + (difference < 0 ? Math.max(difference, -acc) : Math.min(difference, acc)); + spaceObject.setDeltaRotation(finalVel, dirs[i]); + updated = true; + } + } + + if (updated) { + long t = world.getTotalWorldTime(); + if (t - lastRotSyncTick >= 5) { // ~4 Hz + PacketHandler.sendToAll(new PacketStationUpdate(spaceObject, PacketStationUpdate.Type.ROTANGLE_UPDATE)); + lastRotSyncTick = t; + } } } diff --git a/src/main/java/zmaster587/advancedRocketry/tile/station/TileWarpController.java b/src/main/java/zmaster587/advancedRocketry/tile/station/TileWarpController.java index 686d5f18b..69ad9b155 100644 --- a/src/main/java/zmaster587/advancedRocketry/tile/station/TileWarpController.java +++ b/src/main/java/zmaster587/advancedRocketry/tile/station/TileWarpController.java @@ -26,6 +26,7 @@ import zmaster587.advancedRocketry.inventory.modules.ModuleData; import zmaster587.advancedRocketry.inventory.modules.ModulePlanetImage; import zmaster587.advancedRocketry.inventory.modules.ModulePlanetSelector; +import zmaster587.advancedRocketry.item.IDataItem; import zmaster587.advancedRocketry.item.ItemData; import zmaster587.advancedRocketry.item.ItemPlanetIdentificationChip; import zmaster587.advancedRocketry.network.PacketSpaceStationInfo; @@ -68,6 +69,9 @@ public class TileWarpController extends TileEntity implements ITickable, IModula private EmbeddedInventory inv; private ModuleProgress programmingProgress; private int progress; + private int lastSelectedSystem = Constants.INVALID_PLANET; + private long lastClientInfoUpdate = 0L; + public TileWarpController() { tabModule = new ModuleTab(4, 0, 0, this, 3, new String[]{LibVulpes.proxy.getLocalizedString("msg.warpmon.tab.warp"), LibVulpes.proxy.getLocalizedString("msg.warpmon.tab.data"), LibVulpes.proxy.getLocalizedString("msg.warpmon.tab.tracking")}, new ResourceLocation[][]{TextureResources.tabWarp, TextureResources.tabData, TextureResources.tabPlanetTracking}); @@ -196,8 +200,13 @@ public List getModules(int ID, EntityPlayer player) { ISpaceObject station = getSpaceObject(); boolean isOnStation = station != null; - if (world.isRemote) - setPlanetModuleInfo(); + if (world.isRemote) { + long now = System.currentTimeMillis(); + if (now - lastClientInfoUpdate > 200) { + setPlanetModuleInfo(); + lastClientInfoUpdate = now; + } + } //Source planet int baseX = 10; @@ -210,7 +219,7 @@ public List getModules(int ID, EntityPlayer player) { modules.add(srcPlanetImg); - ModuleText text = new ModuleText(baseX + 4, baseY + 4, "Orbiting:", 0xFFFFFF); + ModuleText text = new ModuleText(baseX + 4, baseY + 4, LibVulpes.proxy.getLocalizedString("msg.warpmon.orbit"), 0xFFFFFF); text.setAlwaysOnTop(true); modules.add(text); @@ -308,7 +317,14 @@ public List getModules(int ID, EntityPlayer player) { int starId = 0; if (station != null) starId = station.getProperties().getParentProperties().getStar().getId(); - container = new ModulePlanetSelector(starId, zmaster587.libVulpes.inventory.TextureResources.starryBG, this, this, true); + container = new ModulePlanetSelector( + starId, + zmaster587.libVulpes.inventory.TextureResources.starryBG, + this, + (IProgressBar) this, + (IPlanetDefiner) this, + true + ); container.setOffset(1000, 1000); container.setAllowStarSelection(true); modules.add(container); @@ -407,7 +423,7 @@ private void setPlanetModuleInfo() { @Override public String getModularInventoryName() { - return AdvancedRocketryBlocks.blockWarpShipMonitor.getLocalizedName(); + return ""; } @Override @@ -433,7 +449,7 @@ else if (buttonId == 1) { @Override public void writeDataToNetwork(ByteBuf out, byte id) { if (id == 1 || id == 3) - out.writeInt(container.getSelectedSystem()); + out.writeInt(lastSelectedSystem); else if (id == TAB_SWITCH) out.writeShort(tabModule.getTab()); else if (id >= 10 && id < 20) { @@ -551,24 +567,36 @@ public void onSelectionConfirmed(Object sender) { @Override public void onSelected(Object sender) { - selectSystem(container.getSelectedSystem()); + if (sender instanceof ModulePlanetSelector) { + int id = ((ModulePlanetSelector) sender).getSelectedSystem(); + lastSelectedSystem = id; + selectSystem(id); + } } + private void selectSystem(int id) { - if (getSpaceObject().getOrbitingPlanetId() == SpaceObjectManager.WARPDIMID || id == SpaceObjectManager.WARPDIMID) + // Cache the space object once to avoid repeated lookups + NPE risk + SpaceStationObject so = getSpaceObject(); + if (so == null) { dimCache = null; - else { - dimCache = DimensionManager.getInstance().getDimensionProperties(container.getSelectedSystem()); - - ISpaceObject station = SpaceObjectManager.getSpaceManager().getSpaceStationFromBlockCoords(this.getPos()); - if (station != null) { - station.setDestOrbitingBody(id); - } + return; + } + // If either side is the warp dimension, clear cache and don't compute stats + if (so.getOrbitingPlanetId() == SpaceObjectManager.WARPDIMID || id == SpaceObjectManager.WARPDIMID) { + dimCache = null; + return; } + dimCache = DimensionManager.getInstance().getDimensionProperties(id); + so.setDestOrbitingBody(id); } + @Override public void onSystemFocusChanged(Object sender) { + if (sender instanceof ModulePlanetSelector) { + lastSelectedSystem = ((ModulePlanetSelector) sender).getSelectedSystem(); + } PacketHandler.sendToServer(new PacketMachine(this, (byte) 1)); } @@ -805,7 +833,7 @@ public void loadData(int id) { } - if (!stack.isEmpty() && stack.getItem() instanceof ItemData) { + if (!stack.isEmpty() && stack.getItem() instanceof IDataItem) { ItemData item = (ItemData) stack.getItem(); if (item.getDataType(stack) == type) item.removeData(stack, this.addData(item.getData(stack), item.getDataType(stack), EnumFacing.UP, true), type); @@ -832,8 +860,8 @@ public void storeData(int id) { type = DataType.COMPOSITION; } - if (!stack.isEmpty() && stack.getItem() instanceof ItemData) { - ItemData item = (ItemData) stack.getItem(); + if (!stack.isEmpty() && stack.getItem() instanceof IDataItem) { + IDataItem item = (IDataItem) stack.getItem(); data.extractData(item.addData(stack, data.getDataAmount(type), type), type, EnumFacing.UP, true); } @@ -955,4 +983,18 @@ public boolean isStarKnown(StellarBody body) { return spaceStationObject.isStarKnown(body); return false; } + + @Override + public void onChunkUnload() { + super.onChunkUnload(); + station = null; + dimCache = null; + } + + @Override + public void invalidate() { + super.invalidate(); + station = null; + dimCache = null; + } } diff --git a/src/main/java/zmaster587/advancedRocketry/util/AstronomicalBodyHelper.java b/src/main/java/zmaster587/advancedRocketry/util/AstronomicalBodyHelper.java index c015b0ad5..85c167559 100644 --- a/src/main/java/zmaster587/advancedRocketry/util/AstronomicalBodyHelper.java +++ b/src/main/java/zmaster587/advancedRocketry/util/AstronomicalBodyHelper.java @@ -114,7 +114,12 @@ public static int getAverageTemperature(StellarBody star, int orbitalDistance, i * @param orbitalDistance the distance from the star * @return the insolation of the planet relative to Earth insolation */ + private static final double MIN_BRIGHTNESS = 1.0e-9d; + public static double getStellarBrightness(StellarBody star, int orbitalDistance) { + if (star == null || orbitalDistance <= 0) { + return MIN_BRIGHTNESS; + } //Normal stars are 1.0 times this value, black holes with accretion discs emit less and so modify it float lightMultiplier = 1.0f; //Make all values ratios of Earth normal to get ratio compared to Earth @@ -122,17 +127,30 @@ public static double getStellarBrightness(StellarBody star, int orbitalDistance) float planetaryOrbitalRadius = orbitalDistance / 100f; //Check to see if the star is a black hole boolean blackHole = star.isBlackHole(); - for (StellarBody star2 : star.getSubStars()) - if (!star2.isBlackHole()) { - blackHole = false; - break; + Iterable subs = star.getSubStars(); + if (subs != null) { + for (StellarBody star2 : subs) { + if (star2 != null && !star2.isBlackHole()) { + blackHole = false; + break; + } } + } //There's no real easy way to get the light emitted by an accretion disc, so this substitutes if (blackHole) lightMultiplier *= 0.25; //Returns ratio compared to a planet at 1 AU for Sol, because the other values in AR are normalized, //and this works fairly well for hooking into with other mod's solar panels & such - return (lightMultiplier * ((Math.pow(star.getSize(), 2) * Math.pow(normalizedStarTemperature, 4)) / Math.pow(planetaryOrbitalRadius, 2))); + double brightness = + lightMultiplier * + ((Math.pow(star.getSize(), 2) * Math.pow(normalizedStarTemperature, 4)) / + Math.pow(planetaryOrbitalRadius, 2)); + + // Guarantee: never return 0, NaN, or Infinity + if (!Double.isFinite(brightness) || brightness < MIN_BRIGHTNESS) { + return MIN_BRIGHTNESS; + } + return brightness; } /** diff --git a/src/main/java/zmaster587/advancedRocketry/util/StorageChunk.java b/src/main/java/zmaster587/advancedRocketry/util/StorageChunk.java index 48dd09f56..8d1833559 100644 --- a/src/main/java/zmaster587/advancedRocketry/util/StorageChunk.java +++ b/src/main/java/zmaster587/advancedRocketry/util/StorageChunk.java @@ -111,9 +111,17 @@ private static boolean isInventoryBlock(TileEntity tile) { } private static boolean isLiquidContainerBlock(TileEntity tile) { + // Prefer real sides for compatibility + for (EnumFacing f : EnumFacing.VALUES) { + if (tile.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, f)) { + return true; + } + } + // Fallback for unsided/internal handlers return tile.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); } + public void setWeight(int weight) { this.weight = weight; } @@ -168,11 +176,12 @@ public void recalculateStats(StatsRocket stats) { int fuelCapacityBipropellant = 0; int fuelCapacityOxidizer = 0; int fuelCapacityNuclearWorkingFluid = 0; - + int intakePower = 0; float drillPower = 0f; //stats.reset_no_fuel(); stats.reset_no_fuel();// Oh Quarter... you can not keep adding engine and seat locations every launch - + final boolean isSD = (this.entity instanceof zmaster587.advancedRocketry.entity.EntityStationDeployedRocket); + float weight = 0; for (int yCurr = 0; yCurr <= this.sizeY; yCurr++) { @@ -193,18 +202,33 @@ public void recalculateStats(StatsRocket stats) { } //If rocketEngine increaseThrust - if (block instanceof IRocketEngine && (world.getBlockState(belowPos).getBlock().isAir(world.getBlockState(belowPos), world, belowPos) || world.getBlockState(belowPos).getBlock() instanceof BlockLandingPad || world.getBlockState(belowPos).getBlock() == AdvancedRocketryBlocks.blockLaunchpad)) { - if (block instanceof BlockNuclearRocketMotor) { - nuclearWorkingFluidUseMax += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); - thrustNuclearNozzleLimit += ((IRocketEngine) block).getThrust(world, currBlockPos); - } else if (block instanceof BlockBipropellantRocketMotor) { - bipropellantfuelUse += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); - thrustBipropellant += ((IRocketEngine) block).getThrust(world, currBlockPos); - } else if (block instanceof BlockRocketMotor) { - monopropellantfuelUse += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); - thrustMonopropellant += ((IRocketEngine) block).getThrust(world, currBlockPos); + if (block instanceof IRocketEngine) { + boolean eligible; + if (isSD) { + // SD rockets: skip vertical requirements + eligible = true; + } else { + // Legacy vertical rule + IBlockState belowState = world.getBlockState(belowPos); + Block below = belowState.getBlock(); + eligible = below.isAir(belowState, world, belowPos) + || below instanceof BlockLandingPad + || below == AdvancedRocketryBlocks.blockLaunchpad; + } + + if (eligible) { + if (block instanceof BlockNuclearRocketMotor) { + nuclearWorkingFluidUseMax += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); + thrustNuclearNozzleLimit += ((IRocketEngine) block).getThrust(world, currBlockPos); + } else if (block instanceof BlockBipropellantRocketMotor) { + bipropellantfuelUse += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); + thrustBipropellant += ((IRocketEngine) block).getThrust(world, currBlockPos); + } else if (block instanceof BlockRocketMotor) { + monopropellantfuelUse += ((IRocketEngine) block).getFuelConsumptionRate(world, xCurr, yCurr, zCurr); + thrustMonopropellant += ((IRocketEngine) block).getThrust(world, currBlockPos); + } + stats.addEngineLocation(xCurr - (float)this.sizeX/2 + 0.5f, yCurr+0.5f, zCurr - (float)this.sizeZ/2 + 0.5f); } - stats.addEngineLocation(xCurr - (float) this.sizeX /2+0.5f, yCurr+0.5f, zCurr- (float) this.sizeZ /2+0.5f); } if (block instanceof IFuelTank) { @@ -219,8 +243,18 @@ public void recalculateStats(StatsRocket stats) { } } - if (block instanceof IRocketNuclearCore && ((world.getBlockState(belowPos).getBlock() instanceof IRocketNuclearCore) || (world.getBlockState(belowPos).getBlock() instanceof IRocketEngine))) { - thrustNuclearReactorLimit += ((IRocketNuclearCore) block).getMaxThrust(world, currBlockPos); + if (block instanceof IRocketNuclearCore) { + boolean counts; + if (isSD) { + // SD rockets: no vertical stack requirement + counts = true; + } else { + Block below = world.getBlockState(belowPos).getBlock(); + counts = (below instanceof IRocketNuclearCore) || (below instanceof IRocketEngine); + } + if (counts) { + thrustNuclearReactorLimit += ((IRocketNuclearCore) block).getMaxThrust(world, currBlockPos); + } } if (block instanceof BlockSeat && world.getBlockState(abovePos).getBlock().isPassable(world, abovePos)) { @@ -230,24 +264,14 @@ public void recalculateStats(StatsRocket stats) { if (block instanceof IMiningDrill) { drillPower += ((IMiningDrill) block).getMiningSpeed(world, currBlockPos); } + if (block instanceof IIntake) { + intakePower += ((IIntake) block).getIntakeAmt(state); + } if (block.getUnlocalizedName().contains("servicemonitor")) { hasServiceMonitor = true; } - - TileEntity tile = world.getTileEntity(currBlockPos); - if (tile instanceof TileSatelliteHatch) { - if (ARConfiguration.getCurrentConfig().advancedWeightSystem) { - TileSatelliteHatch hatch = (TileSatelliteHatch) tile; - if (hatch.getSatellite() != null) { - weight += hatch.getSatellite().getProperties().getWeight(); - } else if (hatch.getStackInSlot(0).getItem() instanceof ItemPackedStructure) { - ItemPackedStructure struct = (ItemPackedStructure) hatch.getStackInSlot(0).getItem(); - weight += struct.getStructure(hatch.getStackInSlot(0)).getWeight(); - } - } - } - } + } } } } @@ -272,10 +296,42 @@ public void recalculateStats(StatsRocket stats) { stats.setFuelCapacity(FuelRegistry.FuelType.LIQUID_OXIDIZER, fuelCapacityOxidizer); stats.setFuelCapacity(FuelRegistry.FuelType.NUCLEAR_WORKING_FLUID, fuelCapacityNuclearWorkingFluid); - //Non-fuel stats + // SAFE liquid capacity sum (saturating at Integer.MAX_VALUE) + long liquidCapacitySum = 0L; + + outer: + for (TileEntity te : this.getFluidTiles()) { + net.minecraftforge.fluids.capability.IFluidHandler fh = + te.getCapability(net.minecraftforge.fluids.capability.CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); + if (fh == null) continue; + + net.minecraftforge.fluids.capability.IFluidTankProperties[] props = fh.getTankProperties(); + if (props == null) continue; + + for (net.minecraftforge.fluids.capability.IFluidTankProperties p : props) { + if (p == null) continue; + long cap = Math.max(0L, (long) p.getCapacity()); // guard negatives + if (cap == 0L) continue; + + long next = liquidCapacitySum + cap; // saturating add + if (next >= (long) Integer.MAX_VALUE) { + liquidCapacitySum = (long) Integer.MAX_VALUE; + break outer; // early exit once saturated + } + liquidCapacitySum = next; + } + } + + int liquidCapacitySafe = (int) Math.max(0L, Math.min(liquidCapacitySum, (long) Integer.MAX_VALUE)); + stats.setStatTag("liquidCapacity", liquidCapacitySafe); + + + //Non-fuel stats (keep these after the capacity/tag work) stats.setWeight(weight); stats.setThrust(Math.max(Math.max(thrustMonopropellant, thrustBipropellant), thrustNuclearTotalLimit)); stats.setDrillingPower(drillPower); + stats.setStatTag("intakePower", intakePower); + // (liquidCapacity already set above) } public void addTileEntity(TileEntity te) { diff --git a/src/main/java/zmaster587/advancedRocketry/util/XMLOreLoader.java b/src/main/java/zmaster587/advancedRocketry/util/XMLOreLoader.java index 838205289..a7423e216 100644 --- a/src/main/java/zmaster587/advancedRocketry/util/XMLOreLoader.java +++ b/src/main/java/zmaster587/advancedRocketry/util/XMLOreLoader.java @@ -243,19 +243,19 @@ public boolean loadFile(File xmlFile) throws IOException { * @return list of singleEntry (order MUST be preserved) */ public List> loadPropertyFile() { - Node childNode = doc.getFirstChild(); + List> mapping = new LinkedList<>(); - while (childNode != null) { - if (!childNode.getNodeName().equalsIgnoreCase("oreconfig")) { - childNode = childNode.getFirstChild(); - break; - } + if (doc == null) { + return mapping; + } - childNode = childNode.getNextSibling(); + Node root = doc.getDocumentElement(); + if (root == null || !root.getNodeName().equalsIgnoreCase("oreconfig")) { + AdvancedRocketry.logger.warn("Invalid ore config root node, expected "); + return mapping; } - List> mapping = new LinkedList<>(); - OreGenProperties properties; + Node childNode = root.getFirstChild(); while (childNode != null) { @@ -264,59 +264,61 @@ public List> loadPropertyFile continue; } - if (childNode.hasAttributes()) { - int pressure = -1; - int temp = -1; - NamedNodeMap att = childNode.getAttributes(); - - Node node = att.getNamedItem("pressure"); - - if (node != null) { - try { - pressure = MathHelper.clamp(Integer.parseInt(node.getTextContent()), 0, AtmosphereTypes.values().length); - } catch (NumberFormatException e) { - AdvancedRocketry.logger.warn("Invalid format for pressure: \"" + node.getTextContent() + "\" Only numbers are allowed(" + doc.getDocumentURI() + ")"); - childNode = childNode.getNextSibling(); - continue; - } - } - - node = att.getNamedItem("temp"); - - if (node != null) { - try { - temp = MathHelper.clamp(Integer.parseInt(node.getTextContent()), 0, Temps.values().length); - } catch (NumberFormatException e) { - AdvancedRocketry.logger.warn("Invalid format for temp: \"" + node.getTextContent() + "\" Only numbers are allowed(" + doc.getDocumentURI() + ")"); - childNode = childNode.getNextSibling(); - continue; - } - } - - if (pressure == -1 && temp == -1) { - AdvancedRocketry.logger.warn("Invalid format for temp: \"" + node.getTextContent() + "\" Only numbers are allowed(" + doc.getDocumentURI() + ")"); + int pressure = -1; + int temp = -1; + NamedNodeMap att = childNode.getAttributes(); + + Node node = att.getNamedItem("pressure"); + if (node != null) { + try { + pressure = MathHelper.clamp( + Integer.parseInt(node.getTextContent()), + 0, + AtmosphereTypes.values().length - 1 + ); + } catch (NumberFormatException e) { + AdvancedRocketry.logger.warn("Invalid format for pressure: \"" + node.getTextContent() + "\" Only numbers are allowed (" + doc.getDocumentURI() + ")"); childNode = childNode.getNextSibling(); continue; } + } - properties = loadOre(childNode); - - if (properties == null) { + node = att.getNamedItem("temp"); + if (node != null) { + try { + temp = MathHelper.clamp( + Integer.parseInt(node.getTextContent()), + 0, + Temps.values().length - 1 + ); + } catch (NumberFormatException e) { + AdvancedRocketry.logger.warn("Invalid format for temp: \"" + node.getTextContent() + "\" Only numbers are allowed (" + doc.getDocumentURI() + ")"); childNode = childNode.getNextSibling(); continue; } + } - if (temp != pressure) { - if (pressure == -1) { - mapping.add(new SingleEntry(new HashedBlockPosition(-1, temp, 0), properties)); - } else if (temp == -1) { - mapping.add(new SingleEntry(new HashedBlockPosition(pressure, -1, 0), properties)); - } - } else - mapping.add(new SingleEntry(new HashedBlockPosition(pressure, temp, 0), properties)); + if (pressure == -1 && temp == -1) { + AdvancedRocketry.logger.warn("Each must define at least one of: pressure or temp (" + doc.getDocumentURI() + ")"); + childNode = childNode.getNextSibling(); + continue; + } + OreGenProperties properties = loadOre(childNode); + if (properties == null) { childNode = childNode.getNextSibling(); + continue; } + + if (pressure != -1 && temp != -1) { + mapping.add(new SingleEntry<>(new HashedBlockPosition(pressure, temp, 0), properties)); + } else if (pressure != -1) { + mapping.add(new SingleEntry<>(new HashedBlockPosition(pressure, -1, 0), properties)); + } else { + mapping.add(new SingleEntry<>(new HashedBlockPosition(-1, temp, 0), properties)); + } + + childNode = childNode.getNextSibling(); } return mapping; diff --git a/src/main/java/zmaster587/advancedRocketry/util/XMLPlanetLoader.java b/src/main/java/zmaster587/advancedRocketry/util/XMLPlanetLoader.java index 97cc635a3..8532f91c3 100644 --- a/src/main/java/zmaster587/advancedRocketry/util/XMLPlanetLoader.java +++ b/src/main/java/zmaster587/advancedRocketry/util/XMLPlanetLoader.java @@ -270,10 +270,10 @@ private static Node writePlanet(Document doc, DimensionProperties properties) { nodePlanet.appendChild(createTextNode(doc, ELEMENT_PERIOD, properties.rotationalPeriod)); nodePlanet.appendChild(createTextNode(doc, ELEMENT_ATMDENSITY, properties.getAtmosphereDensity())); // Custom weather properties - nodePlanet.appendChild(createTextNode(doc, ELEMENT_RAIN_START_LENGTH, properties.rainStartLength)); - nodePlanet.appendChild(createTextNode(doc, ELEMENT_RAIN_PROLONGATION_LENGTH, properties.rainProlongationLength)); - nodePlanet.appendChild(createTextNode(doc, ELEMENT_THUNDER_START_LENGTH, properties.thunderStartLength)); - nodePlanet.appendChild(createTextNode(doc, ELEMENT_THUNDER_PROLONGATION_LENGTH, properties.thunderProlongationLength)); + nodePlanet.appendChild(createTextNode(doc, ELEMENT_RAIN_START_LENGTH, properties.getRainStartLength())); + nodePlanet.appendChild(createTextNode(doc, ELEMENT_RAIN_PROLONGATION_LENGTH, properties.getRainProlongationLength())); + nodePlanet.appendChild(createTextNode(doc, ELEMENT_THUNDER_START_LENGTH, properties.getThunderStartLength())); + nodePlanet.appendChild(createTextNode(doc, ELEMENT_THUNDER_PROLONGATION_LENGTH, properties.getThunderProlongationLength())); nodePlanet.appendChild(createTextNode(doc, ELEMENT_RAIN_MARKER, properties.getRainMarker())); nodePlanet.appendChild(createTextNode(doc, ELEMENT_THUNDER_MARKER, properties.getThunderMarker())); @@ -602,14 +602,14 @@ else if (planetPropertyNode.getNodeName().equalsIgnoreCase(ELEMENT_COLOR_OVERRID else if (planetPropertyNode.getNodeName().equalsIgnoreCase(ELEMENT_SKYOVERRIDE)) properties.skyRenderOverride = Boolean.parseBoolean(planetPropertyNode.getTextContent()); else if (planetPropertyNode.getNodeName().equalsIgnoreCase(ELEMENT_RAIN_START_LENGTH)) - properties.rainStartLength = Integer.parseInt(planetPropertyNode.getTextContent()); + properties.setRainStartLength(Integer.parseInt(planetPropertyNode.getTextContent())); // TODO Create default values for new fields else if (planetPropertyNode.getNodeName().equalsIgnoreCase(ELEMENT_RAIN_PROLONGATION_LENGTH)) - properties.rainProlongationLength = Integer.parseInt(planetPropertyNode.getTextContent()); + properties.setRainProlongationLength(Integer.parseInt(planetPropertyNode.getTextContent())); else if (planetPropertyNode.getNodeName().equalsIgnoreCase(ELEMENT_THUNDER_START_LENGTH)) - properties.thunderStartLength = Integer.parseInt(planetPropertyNode.getTextContent()); + properties.setThunderStartLength(Integer.parseInt(planetPropertyNode.getTextContent())); else if (planetPropertyNode.getNodeName().equalsIgnoreCase(ELEMENT_THUNDER_PROLONGATION_LENGTH)) - properties.thunderProlongationLength = Integer.parseInt(planetPropertyNode.getTextContent()); + properties.setThunderProlongationLength(Integer.parseInt(planetPropertyNode.getTextContent())); else if (planetPropertyNode.getNodeName().equalsIgnoreCase(ELEMENT_RAIN_MARKER)) properties.setRainMarker(Integer.parseInt(planetPropertyNode.getTextContent())); else if (planetPropertyNode.getNodeName().equalsIgnoreCase(ELEMENT_THUNDER_MARKER)) diff --git a/src/main/java/zmaster587/advancedRocketry/util/nbt/NBTHelper.java b/src/main/java/zmaster587/advancedRocketry/util/nbt/NBTHelper.java index 573911ce8..caa89dc99 100644 --- a/src/main/java/zmaster587/advancedRocketry/util/nbt/NBTHelper.java +++ b/src/main/java/zmaster587/advancedRocketry/util/nbt/NBTHelper.java @@ -153,7 +153,7 @@ public static Object read(String key, NBTTagCompound compound) { } public static NBTTagList getTagList(String name, NBTTagCompound compound) { - NBTBase nbt = compound.tagMap.get(name); + NBTBase nbt = compound.getTag(name); if (!(nbt instanceof NBTTagList)) { throw new IllegalArgumentException("Tag got by name " + name + "isn't NBTTagList!"); } diff --git a/src/main/java/zmaster587/advancedRocketry/world/ChunkProviderAsteroids.java b/src/main/java/zmaster587/advancedRocketry/world/ChunkProviderAsteroids.java index 2952885cb..8da0ef5c6 100644 --- a/src/main/java/zmaster587/advancedRocketry/world/ChunkProviderAsteroids.java +++ b/src/main/java/zmaster587/advancedRocketry/world/ChunkProviderAsteroids.java @@ -270,10 +270,8 @@ public Chunk generateChunk(int x, int z) { this.rand.setSeed((long) x * 341873128712L + (long) z * 132897987541L); ChunkPrimer chunkprimer = new ChunkPrimer(); - //this.makeasteroids(x, z, chunkprimer); this.prepareHeights(x, z, 0, chunkprimer); this.prepareHeights(x + 500, z + 500, 100, chunkprimer); - //this.genNetherCaves.generate(this.world, x, z, chunkprimer); Chunk chunk = new Chunk(this.world, chunkprimer, x, z); Biome[] abiome = this.world.getBiomeProvider().getBiomes(null, x * 16, z * 16, 16, 16); @@ -283,7 +281,9 @@ public Chunk generateChunk(int x, int z) { abyte[i] = (byte) Biome.getIdForBiome(abiome[i]); } - chunk.setLightPopulated(true); + // this making the black spots... ? + //chunk.setLightPopulated(true); + chunk.generateSkylightMap(); return chunk; } @@ -322,7 +322,7 @@ public boolean isInsideStructure(World worldIn, String structureName, BlockPos p @Override public void populate(int x, int z) { - net.minecraftforge.event.ForgeEventFactory.onChunkPopulate(false, this, this.world, this.rand, x, z, false); + net.minecraftforge.event.ForgeEventFactory.onChunkPopulate(true, this, this.world, this.rand, x, z, false); OreGenProperties oreGenProperties = DimensionManager.getInstance().getDimensionProperties(this.world.provider.getDimension()).getOreGenProperties(this.world); @@ -331,8 +331,7 @@ public void populate(int x, int z) { new CustomizableOreGen(entry).generate(rand, x, z, this.world, this, this.world.getChunkProvider()); } } - + net.minecraftforge.event.ForgeEventFactory.onChunkPopulate(false, this, this.world, this.rand, x, z, false); BlockFalling.fallInstantly = false; - } } diff --git a/src/main/java/zmaster587/advancedRocketry/world/CustomDerivedWorldInfo.java b/src/main/java/zmaster587/advancedRocketry/world/CustomDerivedWorldInfo.java index 71a5ba2bc..c0ebaeb3c 100644 --- a/src/main/java/zmaster587/advancedRocketry/world/CustomDerivedWorldInfo.java +++ b/src/main/java/zmaster587/advancedRocketry/world/CustomDerivedWorldInfo.java @@ -3,6 +3,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.math.BlockPos; import net.minecraft.world.*; +import net.minecraft.world.storage.MapStorage; import net.minecraft.world.storage.WorldInfo; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -12,22 +13,20 @@ public class CustomDerivedWorldInfo extends WorldInfo { private final WorldInfo delegate; - private World world; - private WorldInfoSavedData saveHandler; + private final WorldInfoSavedData saveHandler; - public CustomDerivedWorldInfo(WorldInfo worldInfoIn) { - this.delegate = worldInfoIn; - } - - public void setWorld(World world) { - this.world = world; - } + public CustomDerivedWorldInfo(World world) { + this.delegate = world.getWorldInfo(); - private WorldInfoSavedData getSaveHandler() { + MapStorage worldStorage = world.getPerWorldStorage(); + WorldInfoSavedData saveHandler = (WorldInfoSavedData) worldStorage.getOrLoadData(WorldInfoSavedData.class, WorldInfoSavedData.NAME); if (saveHandler == null) { - saveHandler = (WorldInfoSavedData) world.getPerWorldStorage().getOrLoadData(WorldInfoSavedData.class, "WorldInfoSavedData"); + this.saveHandler = new WorldInfoSavedData(this); + worldStorage.setData(WorldInfoSavedData.NAME, this.saveHandler); + } else { + this.saveHandler = saveHandler; + this.saveHandler.updateWorldInfo(this); } - return saveHandler; } public NBTTagCompound cloneNBTCompound(@Nullable NBTTagCompound nbt) { @@ -177,39 +176,34 @@ public NBTTagCompound getDimensionData(int dimensionID) { @Override public void setCleanWeatherTime(int cleanWeatherTimeIn) { super.setCleanWeatherTime(cleanWeatherTimeIn); - this.getSaveHandler().markDirty(); + saveHandler.markDirty(); } @Override public void setRainTime(int time) { super.setRainTime(time); - this.getSaveHandler().markDirty(); + saveHandler.markDirty(); } @Override public void setThunderTime(int time) { super.setThunderTime(time); - this.getSaveHandler().markDirty(); + saveHandler.markDirty(); } @Override public void setRaining(boolean isRaining) { super.setRaining(isRaining); - this.getSaveHandler().markDirty(); + saveHandler.markDirty(); } @Override public void setThundering(boolean thunderingIn) { super.setThundering(thunderingIn); - this.getSaveHandler().markDirty(); + saveHandler.markDirty(); } - public NBTTagCompound addWeatherData(NBTTagCompound compound) { - compound.setInteger("clearWeatherTime", getCleanWeatherTime()); - compound.setInteger("rainTime", getRainTime()); - compound.setInteger("thunderTime", getThunderTime()); - compound.setBoolean("raining", isRaining()); - compound.setBoolean("thundering", isThundering()); - return compound; + public void injectWeatherData(WorldInfo targetInfo) { + saveHandler.updateWorldInfo(targetInfo); } } diff --git a/src/main/java/zmaster587/advancedRocketry/world/WorldInfoSavedData.java b/src/main/java/zmaster587/advancedRocketry/world/WorldInfoSavedData.java index 2f9832695..d8a0f5b03 100644 --- a/src/main/java/zmaster587/advancedRocketry/world/WorldInfoSavedData.java +++ b/src/main/java/zmaster587/advancedRocketry/world/WorldInfoSavedData.java @@ -1,44 +1,61 @@ package zmaster587.advancedRocketry.world; +import com.google.common.base.Preconditions; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.world.World; import net.minecraft.world.storage.WorldInfo; import net.minecraft.world.storage.WorldSavedData; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + public class WorldInfoSavedData extends WorldSavedData { - private World world; + public static final String NAME = "WorldInfoSavedData"; + + @Nullable + private WorldInfo worldInfo; + @Nullable private WorldInfo readInfo; public WorldInfoSavedData(String name) { super(name); } - public WorldInfoSavedData(World world) { - this("WorldInfoSavedData"); - this.world = world; + public WorldInfoSavedData(CustomDerivedWorldInfo worldInfo) { + this(NAME); + this.worldInfo = worldInfo; this.markDirty(); } @Override - public void readFromNBT(NBTTagCompound nbt) { + public void readFromNBT(@Nonnull NBTTagCompound nbt) { readInfo = new WorldInfo(nbt); } + @Nonnull @Override - public NBTTagCompound writeToNBT(NBTTagCompound compound) { - return ((CustomDerivedWorldInfo) world.getWorldInfo()).addWeatherData(compound); + public NBTTagCompound writeToNBT(@Nonnull NBTTagCompound compound) { + Preconditions.checkNotNull(worldInfo); + + return addWeatherData(worldInfo, compound); } - public void updateWorldInfo(World world) { + public static NBTTagCompound addWeatherData(@Nonnull WorldInfo info, @Nonnull NBTTagCompound compound) { + compound.setInteger("clearWeatherTime", info.getCleanWeatherTime()); + compound.setInteger("rainTime", info.getRainTime()); + compound.setInteger("thunderTime", info.getThunderTime()); + compound.setBoolean("raining", info.isRaining()); + compound.setBoolean("thundering", info.isThundering()); + return compound; + } + + public void updateWorldInfo(WorldInfo target) { if (readInfo == null) { // WorldSavedData not loaded from NBT return; } - this.world = world; - - WorldInfo target = world.getWorldInfo(); + this.worldInfo = target; target.setCleanWeatherTime(readInfo.getCleanWeatherTime()); target.setRaining(readInfo.isRaining()); diff --git a/src/main/java/zmaster587/advancedRocketry/world/WorldServerNotMulti.java b/src/main/java/zmaster587/advancedRocketry/world/WorldServerNotMulti.java deleted file mode 100644 index cf3f46408..000000000 --- a/src/main/java/zmaster587/advancedRocketry/world/WorldServerNotMulti.java +++ /dev/null @@ -1,94 +0,0 @@ -package zmaster587.advancedRocketry.world; - -import net.minecraft.profiler.Profiler; -import net.minecraft.server.MinecraftServer; -import net.minecraft.village.VillageCollection; -import net.minecraft.world.MinecraftException; -import net.minecraft.world.World; -import net.minecraft.world.WorldServer; -import net.minecraft.world.border.IBorderListener; -import net.minecraft.world.border.WorldBorder; -import net.minecraft.world.storage.ISaveHandler; - -public class WorldServerNotMulti extends WorldServer { - private final WorldServer delegate; - private final IBorderListener borderListener; - - public WorldServerNotMulti(MinecraftServer server, ISaveHandler saveHandlerIn, int dimensionId, WorldServer delegate, Profiler profilerIn) { - super(server, saveHandlerIn, new CustomDerivedWorldInfo(delegate.getWorldInfo()), dimensionId, profilerIn); - ((CustomDerivedWorldInfo) this.getWorldInfo()).setWorld(this); - this.delegate = delegate; - this.borderListener = new IBorderListener() { - public void onSizeChanged(WorldBorder border, double newSize) { - WorldServerNotMulti.this.getWorldBorder().setTransition(newSize); - } - - public void onTransitionStarted(WorldBorder border, double oldSize, double newSize, long time) { - WorldServerNotMulti.this.getWorldBorder().setTransition(oldSize, newSize, time); - } - - public void onCenterChanged(WorldBorder border, double x, double z) { - WorldServerNotMulti.this.getWorldBorder().setCenter(x, z); - } - - public void onWarningTimeChanged(WorldBorder border, int newTime) { - WorldServerNotMulti.this.getWorldBorder().setWarningTime(newTime); - } - - public void onWarningDistanceChanged(WorldBorder border, int newDistance) { - WorldServerNotMulti.this.getWorldBorder().setWarningDistance(newDistance); - } - - public void onDamageAmountChanged(WorldBorder border, double newAmount) { - WorldServerNotMulti.this.getWorldBorder().setDamageAmount(newAmount); - } - - public void onDamageBufferChanged(WorldBorder border, double newSize) { - WorldServerNotMulti.this.getWorldBorder().setDamageBuffer(newSize); - } - }; - this.delegate.getWorldBorder().addListener(this.borderListener); - } - - @Override - protected void saveLevel() throws MinecraftException { - this.perWorldStorage.saveAllData(); - } - - public World init() { - super.init(); - // load weather data from NBT - WorldInfoSavedData wi = (WorldInfoSavedData) perWorldStorage.getOrLoadData(WorldInfoSavedData.class, "WorldInfoSavedData"); - if (wi == null) { - wi = new WorldInfoSavedData(this); - this.perWorldStorage.setData("WorldInfoSavedData", wi); - } - wi.updateWorldInfo(this); - - this.mapStorage = this.delegate.getMapStorage(); - this.worldScoreboard = this.delegate.getScoreboard(); - this.lootTable = this.delegate.getLootTableManager(); - this.advancementManager = this.delegate.getAdvancementManager(); - String s = VillageCollection.fileNameForProvider(this.provider); - VillageCollection villagecollection = (VillageCollection) this.perWorldStorage.getOrLoadData(VillageCollection.class, s); - - if (villagecollection == null) { - this.villageCollection = new VillageCollection(this); - this.perWorldStorage.setData(s, this.villageCollection); - } else { - this.villageCollection = villagecollection; - this.villageCollection.setWorldsForAll(this); - } - - this.initCapabilities(); - return this; - } - - - @Override - public void flush() { - super.flush(); - this.delegate.getWorldBorder().removeListener(this.borderListener); // Unlink ourselves, to prevent world leak. - this.provider.onWorldSave(); - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/world/biome/BiomeGenPumpkin.java b/src/main/java/zmaster587/advancedRocketry/world/biome/BiomeGenPumpkin.java deleted file mode 100644 index b9b4f6f86..000000000 --- a/src/main/java/zmaster587/advancedRocketry/world/biome/BiomeGenPumpkin.java +++ /dev/null @@ -1,47 +0,0 @@ -package zmaster587.advancedRocketry.world.biome; - -import net.minecraft.entity.monster.EntitySkeleton; -import net.minecraft.init.Blocks; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.biome.Biome; - -public class BiomeGenPumpkin extends Biome { - - public BiomeGenPumpkin(int biomeId, boolean register) { - super(new BiomeProperties("Pumpkin").setBaseHeight(1f).setHeightVariation(0.1f).setTemperature(0.9f).setRainDisabled()); - - //cold and dry - registerBiome(biomeId, "Pumpkin", this); - - - this.decorator.generateFalls = false; - this.decorator.flowersPerChunk = 0; - this.decorator.grassPerChunk = 5; - this.decorator.treesPerChunk = 0; - this.fillerBlock = Blocks.DIRT.getDefaultState(); - this.topBlock = Blocks.PUMPKIN.getDefaultState(); - - this.spawnableMonsterList.clear(); - this.spawnableWaterCreatureList.clear(); - this.spawnableCaveCreatureList.clear(); - this.spawnableCreatureList.clear(); - this.spawnableMonsterList.add(new Biome.SpawnListEntry(EntitySkeleton.class, 10, 1, 10)); - } - - @Override - public int getFoliageColorAtPos(BlockPos pos) { - int color = 0x953929; - return getModdedBiomeFoliageColor(color); - } - - @Override - public int getGrassColorAtPos(BlockPos pos) { - int color = 0x953929; - return getModdedBiomeFoliageColor(color); - } - - @Override - public float getSpawningChance() { - return 0.7f; //Nothing spawns - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/world/biome/BiomeGenWatermelon.java b/src/main/java/zmaster587/advancedRocketry/world/biome/BiomeGenWatermelon.java deleted file mode 100644 index 1f45da8ae..000000000 --- a/src/main/java/zmaster587/advancedRocketry/world/biome/BiomeGenWatermelon.java +++ /dev/null @@ -1,29 +0,0 @@ -package zmaster587.advancedRocketry.world.biome; - -import net.minecraft.entity.monster.EntityEnderman; -import net.minecraft.init.Blocks; -import net.minecraft.world.biome.Biome; - -public class BiomeGenWatermelon extends Biome { - - public BiomeGenWatermelon(int biomeId, boolean register) { - super(new BiomeProperties("Watermelon").setBaseHeight(1f).setHeightVariation(0.1f).setTemperature(0.9f).setRainDisabled()); - - //cold and dry - - this.decorator.generateFalls = false; - this.decorator.flowersPerChunk = 0; - this.decorator.grassPerChunk = 0; - this.decorator.treesPerChunk = 0; - this.fillerBlock = this.topBlock = Blocks.MELON_BLOCK.getDefaultState(); - - this.spawnableMonsterList.clear(); - this.spawnableMonsterList.add(new Biome.SpawnListEntry(EntityEnderman.class, 10, 1, 10)); - } - - - @Override - public float getSpawningChance() { - return 0.7f; //Nothing spawns - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/world/provider/WorldProviderPlanet.java b/src/main/java/zmaster587/advancedRocketry/world/provider/WorldProviderPlanet.java index f71ef4689..24068481a 100644 --- a/src/main/java/zmaster587/advancedRocketry/world/provider/WorldProviderPlanet.java +++ b/src/main/java/zmaster587/advancedRocketry/world/provider/WorldProviderPlanet.java @@ -40,8 +40,7 @@ public class WorldProviderPlanet extends WorldProvider implements IPlanetaryProvider { - - /*@Override +/*@Override protected void registerWorldChunkManager() { //this.worldChunkMgr = new WorldChunkManagerHell(BiomeGenBase.extremeHills, 0.0f); this.worldChunkMgr = new ChunkManagerPlanet(getSeed(), planetWorldType); @@ -115,6 +114,10 @@ public void calculateInitialWeather() { @Override public void updateWeather() { DimensionProperties props = getDimensionProperties(); + if (!props.usesCustomWorldInfo()) { + super.updateWeather(); + return; + } // Totally override weather cycle if (world.provider.hasSkyLight()) { @@ -122,6 +125,7 @@ public void updateWeather() { boolean flag = world.getGameRules().getBoolean("doWeatherCycle"); if (flag) { + // No rain or thunder if (props.getRainMarker() == -1 && props.getThunderMarker() == -1) { world.getWorldInfo().setRaining(false); world.getWorldInfo().setRainTime(0); @@ -147,13 +151,20 @@ public void updateWeather() { world.getWorldInfo().setRaining(true); } + // Clamp to avoid IllegalArgumentException in Random#nextInt(0 or negative) + final int thunderProlong = props.getThunderProlongationLength() > 0 ? props.getThunderProlongationLength() : 12000; + final int thunderStart = props.getThunderStartLength() > 0 ? props.getThunderStartLength() : 168000; + final int rainProlong = props.getRainProlongationLength() > 0 ? props.getRainProlongationLength() : 12000; + final int rainStart = props.getRainStartLength() > 0 ? props.getRainStartLength() : 168000; + + int k2 = world.getWorldInfo().getThunderTime(); if (k2 <= 0) { if (world.getWorldInfo().isThundering()) { - world.getWorldInfo().setThunderTime(world.rand.nextInt(getDimensionProperties().thunderProlongationLength) + 3600); + world.getWorldInfo().setThunderTime(world.rand.nextInt(thunderProlong) + 3600); } else { - world.getWorldInfo().setThunderTime(world.rand.nextInt(getDimensionProperties().thunderStartLength) + 12000); + world.getWorldInfo().setThunderTime(world.rand.nextInt(thunderStart) + 12000); } } else { --k2; @@ -168,9 +179,9 @@ public void updateWeather() { if (l2 <= 0) { if (world.getWorldInfo().isRaining()) { - world.getWorldInfo().setRainTime(world.rand.nextInt(getDimensionProperties().rainProlongationLength) + 12000); + world.getWorldInfo().setRainTime(world.rand.nextInt(rainProlong) + 12000); } else { - world.getWorldInfo().setRainTime(world.rand.nextInt(getDimensionProperties().rainStartLength) + 12000); + world.getWorldInfo().setRainTime(world.rand.nextInt(rainStart) + 12000); } } else { --l2; @@ -182,6 +193,7 @@ public void updateWeather() { } } + world.prevThunderingStrength = world.thunderingStrength; if (world.getWorldInfo().isThundering()) { diff --git a/src/main/java/zmaster587/advancedRocketry/world/util/BasicTeleporter.java b/src/main/java/zmaster587/advancedRocketry/world/util/BasicTeleporter.java new file mode 100644 index 000000000..20b346ffc --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/world/util/BasicTeleporter.java @@ -0,0 +1,23 @@ +package zmaster587.advancedRocketry.world.util; + +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ITeleporter; + +public class BasicTeleporter implements ITeleporter { + private final BlockPos basePos; + + public BasicTeleporter(BlockPos basePos) { + this.basePos = basePos; + } + + protected BlockPos getTargetPos(World world) { + return basePos; + } + + @Override + public void placeEntity(World world, Entity entity, float yaw) { + entity.moveToBlockPosAndAngles(getTargetPos(world), yaw, entity.rotationPitch); + } +} diff --git a/src/main/java/zmaster587/advancedRocketry/world/util/MultiData.java b/src/main/java/zmaster587/advancedRocketry/world/util/MultiData.java index f523f032d..27b4178d5 100644 --- a/src/main/java/zmaster587/advancedRocketry/world/util/MultiData.java +++ b/src/main/java/zmaster587/advancedRocketry/world/util/MultiData.java @@ -19,6 +19,14 @@ public MultiData() { reset(); } + private static final java.util.EnumSet SUPPORTED_TYPES = + java.util.EnumSet.of( + DataStorage.DataType.COMPOSITION, + DataStorage.DataType.MASS, + DataStorage.DataType.DISTANCE + ); + + public void reset() { for (DataStorage.DataType type : DataStorage.DataType.values()) { if (type != DataStorage.DataType.UNDEFINED) @@ -91,4 +99,4 @@ public void readFromNBT(NBTTagCompound nbt) { } } } -} \ No newline at end of file +} diff --git a/src/main/java/zmaster587/advancedRocketry/world/util/TeleporterNoPortal.java b/src/main/java/zmaster587/advancedRocketry/world/util/TeleporterNoPortal.java deleted file mode 100644 index 6b2457ed3..000000000 --- a/src/main/java/zmaster587/advancedRocketry/world/util/TeleporterNoPortal.java +++ /dev/null @@ -1,37 +0,0 @@ -package zmaster587.advancedRocketry.world.util; - -import net.minecraft.entity.Entity; -import net.minecraft.world.Teleporter; -import net.minecraft.world.WorldServer; - -public class TeleporterNoPortal extends Teleporter { - - public TeleporterNoPortal(WorldServer p_i1963_1_) { - super(p_i1963_1_); - } - - public void teleport(Entity entity, WorldServer world) { - - if (entity.isEntityAlive()) { - entity.setLocationAndAngles(entity.posX, entity.posY, entity.posZ, entity.rotationYaw, entity.rotationPitch); - world.spawnEntity(entity); - world.updateEntityWithOptionalForce(entity, false); - } - entity.setWorld(world); - } - - @Override - public boolean placeInExistingPortal(Entity entityIn, float rotationYaw) { - return false; - } - - @Override - public void removeStalePortalLocations(long par1) { - } - - - @Override - public boolean makePortal(Entity p_85188_1_) { - return true; - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/world/util/TeleporterNoPortalSeekBlock.java b/src/main/java/zmaster587/advancedRocketry/world/util/TeleporterNoPortalSeekBlock.java deleted file mode 100644 index 294a5aeca..000000000 --- a/src/main/java/zmaster587/advancedRocketry/world/util/TeleporterNoPortalSeekBlock.java +++ /dev/null @@ -1,60 +0,0 @@ -package zmaster587.advancedRocketry.world.util; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.util.math.BlockPos.MutableBlockPos; -import net.minecraft.world.Teleporter; -import net.minecraft.world.WorldServer; - -public class TeleporterNoPortalSeekBlock extends Teleporter { - - public TeleporterNoPortalSeekBlock(WorldServer p_i1963_1_) { - super(p_i1963_1_); - } - - public void teleport(Entity entity, WorldServer world) { - - if (entity.isEntityAlive()) { - entity.setLocationAndAngles(entity.posX, entity.posY, entity.posZ, entity.rotationYaw, entity.rotationPitch); - world.spawnEntity(entity); - world.updateEntityWithOptionalForce(entity, false); - } - entity.setWorld(world); - } - - @Override - public boolean placeInExistingPortal(Entity entityIn, float rotationYaw) { - - double x, y, z; - x = entityIn.posX; - y = entityIn.posY; - z = entityIn.posZ; - MutableBlockPos pos = new MutableBlockPos(); - - for (int yy = (int) y; yy < world.getHeight(); yy++) { - pos.setPos(x, yy, z); - if (world.isAirBlock(pos) && world.isAirBlock(pos.add(0, 1, 0))) { - y = yy; - break; - } - } - - if (entityIn instanceof EntityPlayerMP) { - ((EntityPlayerMP) entityIn).connection.setPlayerLocation(x, y, z, entityIn.rotationYaw, entityIn.rotationPitch); - } else { - entityIn.setLocationAndAngles(x, y, z, entityIn.rotationYaw, entityIn.rotationPitch); - } - - return true; - } - - @Override - public void removeStalePortalLocations(long par1) { - } - - - @Override - public boolean makePortal(Entity p_85188_1_) { - return true; - } -} diff --git a/src/main/java/zmaster587/advancedRocketry/world/util/TeleporterSeekBlock.java b/src/main/java/zmaster587/advancedRocketry/world/util/TeleporterSeekBlock.java new file mode 100644 index 000000000..86f972b68 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/world/util/TeleporterSeekBlock.java @@ -0,0 +1,25 @@ +package zmaster587.advancedRocketry.world.util; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockPos.MutableBlockPos; +import net.minecraft.world.World; + +public class TeleporterSeekBlock extends BasicTeleporter { + public TeleporterSeekBlock(BlockPos targetPos) { + super(targetPos); + } + + @Override + protected BlockPos getTargetPos(World world) { + BlockPos pos = super.getTargetPos(world); + MutableBlockPos clearPos = new MutableBlockPos(pos); + + for (int yy = pos.getY(); yy < world.getHeight(); yy++) { + clearPos.setPos(pos.getX(), yy, pos.getZ()); + if (world.isAirBlock(clearPos) && world.isAirBlock(clearPos.add(0, 1, 0))) { + return clearPos; + } + } + return pos; + } +} diff --git a/src/main/resources/META-INF/accessTransformer.cfg b/src/main/resources/META-INF/accessTransformer.cfg deleted file mode 100644 index 21b6200fa..000000000 --- a/src/main/resources/META-INF/accessTransformer.cfg +++ /dev/null @@ -1,10 +0,0 @@ -public net.minecraft.entity.Entity * -public net.minecraft.nbt.NBTTagCompound * -public-f net.minecraft.inventory.InventoryBasic * -public net.minecraft.world.storage.MapStorage * - -public net.minecraft.server.MinecraftServer * -public net.minecraft.server.MinecraftServer *() - -public net.minecraft.server.integrated.IntegratedServer * -public net.minecraft.server.integrated.IntegratedServer *() diff --git a/src/main/resources/advancedrocketry_at.cfg b/src/main/resources/advancedrocketry_at.cfg new file mode 100644 index 000000000..a46c57470 --- /dev/null +++ b/src/main/resources/advancedrocketry_at.cfg @@ -0,0 +1,2 @@ +# For async weather +public net.minecraft.world.World field_72986_A # worldInfo \ No newline at end of file diff --git a/src/main/resources/assets/advancedrocketry/blockstates/databusbig.json b/src/main/resources/assets/advancedrocketry/blockstates/databusbig.json new file mode 100644 index 000000000..0328cba2f --- /dev/null +++ b/src/main/resources/assets/advancedrocketry/blockstates/databusbig.json @@ -0,0 +1,23 @@ +{ + "variants": { + "varient=0": { "model": "advancedrocketry:databusbig" }, + "varient=1": { "model": "advancedrocketry:databusbig" }, + "varient=2": { "model": "advancedrocketry:databusbig" }, + "varient=3": { "model": "advancedrocketry:databusbig" }, + "varient=4": { "model": "advancedrocketry:databusbig" }, + "varient=5": { "model": "advancedrocketry:databusbig" }, + "varient=6": { "model": "advancedrocketry:databusbig" }, + "varient=7": { "model": "advancedrocketry:databusbig" }, + + "varient=8": { "model": "advancedrocketry:databusbig" }, + "varient=9": { "model": "advancedrocketry:databusbig" }, + "varient=10": { "model": "advancedrocketry:databusbig" }, + "varient=11": { "model": "advancedrocketry:databusbig" }, + "varient=12": { "model": "advancedrocketry:databusbig" }, + "varient=13": { "model": "advancedrocketry:databusbig" }, + "varient=14": { "model": "advancedrocketry:databusbig" }, + "varient=15": { "model": "advancedrocketry:databusbig" }, + + "inventory": { "model": "advancedrocketry:databusbig" } + } +} diff --git a/src/main/resources/assets/advancedrocketry/blockstates/orbitalregistry.json b/src/main/resources/assets/advancedrocketry/blockstates/orbitalregistry.json new file mode 100644 index 000000000..dd6a128b9 --- /dev/null +++ b/src/main/resources/assets/advancedrocketry/blockstates/orbitalregistry.json @@ -0,0 +1,20 @@ +{ + "forge_marker": 1, + "defaults": { + "transform": "forge:default-block", + "model": "advancedrocketry:orbitalRegistry" + }, + "variants": { + "facing=north,state=false": [{}], + "facing=south,state=false": { "model": "advancedrocketry:orbitalRegistry", "y": 180 }, + "facing=west,state=false": { "model": "advancedrocketry:orbitalRegistry", "y": 270 }, + "facing=east,state=false": { "model": "advancedrocketry:orbitalRegistry", "y": 90 }, + + "facing=north,state=true": [{}], + "facing=south,state=true": { "model": "advancedrocketry:orbitalRegistry", "y": 180 }, + "facing=west,state=true": { "model": "advancedrocketry:orbitalRegistry", "y": 270 }, + "facing=east,state=true": { "model": "advancedrocketry:orbitalRegistry", "y": 90 }, + + "inventory": [{}] + } +} diff --git a/src/main/resources/assets/advancedrocketry/blockstates/wirelesstransciever.json b/src/main/resources/assets/advancedrocketry/blockstates/wirelesstransciever.json index 72b127360..b2c7287f9 100644 --- a/src/main/resources/assets/advancedrocketry/blockstates/wirelesstransciever.json +++ b/src/main/resources/assets/advancedrocketry/blockstates/wirelesstransciever.json @@ -1,13 +1,19 @@ { - "variants": { - "facing=north,state=false": { "model": "advancedrocketry:wirelesstransciever" }, - "facing=south,state=false": { "model": "advancedrocketry:wirelesstransciever", "y": 180 }, - "facing=west,state=false": { "model": "advancedrocketry:wirelesstransciever", "y": 270 }, - "facing=east,state=false": { "model": "advancedrocketry:wirelesstransciever", "y": 90 }, - "facing=north,state=true": { "model": "advancedrocketry:wirelesstransciever" }, - "facing=south,state=true": { "model": "advancedrocketry:wirelesstransciever", "y": 180 }, - "facing=west,state=true": { "model": "advancedrocketry:wirelesstransciever", "y": 270 }, - "facing=east,state=true": { "model": "advancedrocketry:wirelesstransciever", "y": 90 }, - "inventory" : { "model": "advancedrocketry:wirelesstransciever" } - } + "variants": { + "facing=north,state=false": { "model": "advancedrocketry:wirelesstransciever" }, + "facing=south,state=false": { "model": "advancedrocketry:wirelesstransciever", "y": 180 }, + "facing=west,state=false": { "model": "advancedrocketry:wirelesstransciever", "y": 270 }, + "facing=east,state=false": { "model": "advancedrocketry:wirelesstransciever", "y": 90 }, + "facing=up,state=false": { "model": "advancedrocketry:wirelesstransciever", "x": 270 }, + "facing=down,state=false": { "model": "advancedrocketry:wirelesstransciever", "x": 90 }, + + "facing=north,state=true": { "model": "advancedrocketry:wirelesstransciever" }, + "facing=south,state=true": { "model": "advancedrocketry:wirelesstransciever", "y": 180 }, + "facing=west,state=true": { "model": "advancedrocketry:wirelesstransciever", "y": 270 }, + "facing=east,state=true": { "model": "advancedrocketry:wirelesstransciever", "y": 90 }, + "facing=up,state=true": { "model": "advancedrocketry:wirelesstransciever", "x": 270 }, + "facing=down,state=true": { "model": "advancedrocketry:wirelesstransciever", "x": 90 }, + + "inventory": { "model": "advancedrocketry:wirelesstransciever" } + } } diff --git a/src/main/resources/assets/advancedrocketry/lang/de_DE.lang b/src/main/resources/assets/advancedrocketry/lang/de_DE.lang index c0dc83148..4a6d75c7c 100644 --- a/src/main/resources/assets/advancedrocketry/lang/de_DE.lang +++ b/src/main/resources/assets/advancedrocketry/lang/de_DE.lang @@ -345,6 +345,7 @@ msg.rocketbuilder.fuel=Treibstoff msg.rocketbuilder.acc=Beschleunigung msg.rocketbuilder.build=Bauen msg.rocketbuilder.scan=Scannen +msg.rocketbuilder.alreadyassembled=Rakete bereits zusammengebaut msg.solar.collectingEnergy= Energie sammeln: msg.solar.cannotcollectEnergy=Kann keine Energie sammeln msg.asteroidChip.asteroid=Asteroid @@ -406,4 +407,10 @@ msg.notconnected=Nicht verbunden msg.unprogrammed=Unprogrammiert msg.programfail=Programmierung fehlgeschlagen msg.modules=Module -msg.na=Nicht verfügbar \ No newline at end of file +msg.na=Nicht verfügbar + +jei.sb.satellitepreview=Bereit für den Orbit! +jei.sb.copy.source=Quelle +jei.sb.copy.output=Neue Kopie +jei.sb.assemblyhint=Mindestens ein Solarpanel +jei.sb.copychiphint=Erstelle Sicherungskopien! diff --git a/src/main/resources/assets/advancedrocketry/lang/en_US.lang b/src/main/resources/assets/advancedrocketry/lang/en_US.lang index fc1e8d6b3..4037c59cc 100644 --- a/src/main/resources/assets/advancedrocketry/lang/en_US.lang +++ b/src/main/resources/assets/advancedrocketry/lang/en_US.lang @@ -11,6 +11,7 @@ death.attack.Heat=%1$s died due to overheating death.attack.Heat.player=%1$s died due to overheating entity.advancedRocketry.rocket.name=Rocket entity.rocket.name=Rocket +entity.deployedRocket.name=Rocket entity.hovercraft.name=Hovercraft tile.landingPad.name=Landing Pad @@ -25,14 +26,14 @@ tile.turf.name=Moon Turf tile.turfDark.name=Dark Moon Turf tile.cuttingMachine.name=Cutting Machine tile.sawBlade.name=Saw Blade Assembly -tile.controlComp.name=Mission Control Computer tile.precisionAssemblingMachine.name=Precision Assembler tile.spaceLaser.name=Orbital Laser Drill tile.Crystallizer.name=Crystallizer tile.blastBrick.name=HeatProof Brick tile.blastFurnaceController.name=HeatProof Furnace Controller tile.fuelStation.name=Fueling station -tile.loader.0.name=Data Bus +tile.databusbig.name=Advanced Databus +tile.loader.0.name=Databus tile.loader.1.name=Satellite Bay tile.loader.2.name=Rocket Unloader tile.loader.3.name=Rocket Loader @@ -136,9 +137,10 @@ tile.precisionlaseretcher.name=Precision Laser Etcher tile.enrichedLavaBlock.name=Enriched Lava Block tile.basalt.name=Basalt tile.landingfloat.name=Landing Float -tile.solararray.name=Solar Array +tile.solararray.name=Solar Array Controller tile.solararraypanel.name=Solar Array Panel tile.serviceStation.name=Service Station +tile.orbitalRegistry.name=Orbital Registry item.lens.0.name=Basic Lens item.wafer.0.name=Silicon Wafer @@ -170,7 +172,7 @@ item.miscpart.0.name=User Interface item.miscpart.1.name=Carbon Brick item.station.name=Space Station Container item.stationChip.name=Space Station Id Chip -item.stationchip.openmenu=Crouch right-click to open configuration menu +item.stationchip.openmenu=Sneak right-click to open configuration menu item.spaceHelmet.name=Space Suit Helmet item.spaceChest.name=Space Suit Chest-Piece item.spaceLeggings.name=Space Suit Leggings @@ -187,12 +189,19 @@ item.itemUpgrade.4.name=Anti-Fog Visor item.itemUpgrade.5.name=Earthbright Visor item.atmAnalyser.name=Atmosphere Analyzer item.biomeChanger.name=Biome Changer Remote -item.weatherController.name=Weather Satellite Remote +item.weatherController.name=Weather Remote item.basicLaserGun.name=Basic Laser Gun item.beaconFinder.name=Beacon Finder item.thermite.name=Thermite item.hovercraft.name=Hovercraft -item.hovercraft.tooltip=Long lasting dilithium power source. It'll probably outlive you. +item.satellite.opticaltelescope=Optical Telescope +item.satellite.composition=Composition Scanner +item.satellite.massscanner=Mass Scanner +item.satellite.solar=Solar +item.satellite.oremapper=Ore Mapper +item.satellite.biomechanger=Biome Changer +item.satellite.weather=Weather Satellite + item.jetPack.name=Suit Jetpack item.pressureTank.0.name=Low Pressure Tank @@ -210,13 +219,16 @@ material.TitaniumIridium.name=Titanium Iridium Alloy enchantment.spaceBreathing=Airtight Seal -data.undefined.name=Some Random Data +data.undefined.name=Undefined data.distance.name=Distance data.humidity.name=Humidity data.temperature.name=Temperature data.composition.name=Composition data.atmospheredensity.name=Atmosphere Density data.mass.name=Mass +data.label.type=Type: +data.label.data=Data + fluid.oxygen=Oxygen fluid.hydrogen=Hydrogen @@ -227,9 +239,16 @@ fluid.enrichedLava=Enriched Lava mission.asteroidmining.name=Asteroid Mining mission.gascollection.name=Gas Collection -error.rocket.cannotGetThere=Destination Unreachable -error.rocket.destinationNotExist=Cannot launch: Destination does not exist -error.rocket.notSameSystem=Cannot launch: Destination is not in the same planet system +error.rocket.notEnoughMissionFuel=Not enough fuel! +error.rocket.tooHeavy=Rocket is too heavy to launch (insufficient thrust). +error.rocket.cannotGetThere=Selected destination cannot be reached. (Are you trying to land on a Gas Giant?) +error.rocket.destinationNotExist=Selected space station does not exist. +error.rocket.partsWornOut=Critical parts are worn out — launch aborted. +error.rocket.aborted=Launch aborted. +error.rocket.gatedArtifactMissing=Missing Artifact. (Player Inventory) +error.rocket.gatedArtifactMissingWithItem=Missing required artifact: %sx %s (Player Inventory) +error.rocket.outsideStarSystem=Interstellar travel requires a Starship. +error.rocket.outsidePlanetarySystem=Planetary travel requires a Nuclear Rocket. advancement.holographic=Holographic advancement.holographic.desc=Craft a Holo-Projector @@ -264,8 +283,7 @@ advancement.beerOnTheSun.desc=You'll need more TNT to get to orbit advancement.suitedUp=Suited Up advancement.suitedUp.desc=Wear a full spacesuit - - +# Controls - options key.controls.advancedrocketry=Advanced Rocketry key.openRocketUI=Open Rocket GUI key.toggleJetpack=Toggle Jetpack @@ -279,6 +297,88 @@ enchantment.advancedrocketry.spacebreathing.desc=Allows the piece of armor to fo machine.tooltip.smallplatepress=Requires obsidian two blocks below to function +# Commands +commands.advancedrocketry.invalid=%s %s does not exist! + +commands.advancedrocketry.dev.usage=/advancedrocketry dev help - (developer use only) lists subcommands. +commands.advancedrocketry.dev.dumpbiomes.usage=dumpBiomes - Dumps biome info to BiomeDump.txt +commands.advancedrocketry.dev.dumpbiomes.success=The file 'BiomeDump.txt' has been written to the instance directory +commands.advancedrocketry.dev.runtests.usage=runTests - Runs rocket tests for debug only! + +commands.advancedrocketry.filldata.usage=/advancedrocketry fillData (alias: fd) +commands.advancedrocketry.filldata.invalid=Not a valid datatype, try one of the following: +commands.advancedrocketry.filldata.success=Data filled! +commands.advancedrocketry.filldata.wrongtype=Type does not match stored type, data unchanged + +commands.advancedrocketry.goto.usage=/advancedrocketry goto help - lists subcommands. +commands.advancedrocketry.goto.dimension.usage=goto dimension (alias: d, dim) - teleports the player to the supplied dimension +commands.advancedrocketry.goto.station.usage=goto station (alias: s) - teleports the player to the supplied station + +commands.advancedrocketry.planet.usage=/advancedrocketry planet help - lists subcommands. +commands.advancedrocketry.planet.reset.usage=planet reset [dimId] +commands.advancedrocketry.planet.list.usage=planet list +commands.advancedrocketry.planet.list.dimensions=Dimensions: +commands.advancedrocketry.planet.list.entry=DIM%d: %s +commands.advancedrocketry.planet.delete.usage=planet delete +commands.advancedrocketry.planet.delete.success=Dim %d deleted! +commands.advancedrocketry.planet.delete.invalid=World still has players: +commands.advancedrocketry.planet.generate.usage=planet generate [moon] [gas] [atmosphere base] [distance base] [gravity base] +commands.advancedrocketry.planet.generate.invalid=Dimension: %s failed to generate! +commands.advancedrocketry.planet.generate.success=Dimension: %s generated! +commands.advancedrocketry.planet.set.usage=planet set [dimId] +commands.advancedrocketry.planet.set.success=Successfully set dimension %d's property %s to %s +commands.advancedrocketry.planet.set.invalid=Property lookup failed, please check logs +commands.advancedrocketry.planet.set.mismatch=Cannot set property %s of type %s to value(s) %s +commands.advancedrocketry.planet.set.wronglength=Array property is of length %d, but %d values were passed +commands.advancedrocketry.planet.get.usage=planet get [dimId] +commands.advancedrocketry.planet.get.success=%s=%s + +commands.advancedrocketry.star.usage=/advancedrocketry star help - lists subcommands. +commands.advancedrocketry.star.action.temp.get=Temp: %d +commands.advancedrocketry.star.action.temp.set=Temp set to %d +commands.advancedrocketry.star.action.planets.get=Planets orbiting the star: +commands.advancedrocketry.star.action.planets.get.entry=ID: %d : %s +commands.advancedrocketry.star.action.pos.get=Position: %d, %d +commands.advancedrocketry.star.action.pos.set=Position set to %d, %d +commands.advancedrocketry.star.list.usage=star list +commands.advancedrocketry.star.list.entry=Star ID: %d Name: %s Num Planets: %d +commands.advancedrocketry.star.get.usage=star get +commands.advancedrocketry.star.set.usage=star set +commands.advancedrocketry.star.set.temp.usage=star set temp +commands.advancedrocketry.star.set.pos.usage=star set pos +commands.advancedrocketry.star.generate.usage=star generate +commands.advancedrocketry.star.generate.success=Star added! +commands.advancedrocketry.star.generate.invalid=Why can't I hold all these stars! (either you have an insane number of stars or something really broke!) + +commands.advancedrocketry.station.usage=/advancedrocketry station help - lists subcommands. +commands.advancedrocketry.station.create.usage=create [playerName] [tp] - Creates a new station orbiting and generates a 3x3 cobble platform. If [playerName] is provided, the player will be given a corresponding station id chip. If [tp] is specified, they will also be teleported to the station. +commands.advancedrocketry.station.create.invalid=No AR DimensionProperties for dimId %s +commands.advancedrocketry.station.create.tip=Tip: /advancedrocketry planet list +commands.advancedrocketry.station.create.success=Created station ID %d orbiting dim %d (space @ %d, %d, %d) +commands.advancedrocketry.station.give.usage=give [playerName] - Gives you (or the player [playerName]) a space station with ID + +commands.advancedrocketry.fetch.usage=/advancedrocketry fetch - Teleports the player from any dimension to you + +commands.advancedrocketry.reloadrecipes.usage=/advancedrocketry reloadRecipes - Reloads recipes from the XML files in the config folder +commands.advancedrocketry.reloadrecipes.error1=Serious error has occurred! Possible recipe corruption +commands.advancedrocketry.reloadrecipes.error2=Please check logs! +commands.advancedrocketry.reloadrecipes.error3=You may be able to rectify this error by repairing the XML and/or restarting the game + +commands.advancedrocketry.setgravity.usage=/advancedrocketry setGravity [playerName] - sets your gravity (or the player [playerName]) to where 1 is Earth's, 0 is planet's default + +commands.advancedrocketry.addtorch.usage=/advancedrocketry addTorch - Adds held block to the list of objects that drop when there's no atmosphere +commands.advancedrocketry.addtorch.invalid=Held block cannot be added to torch list +commands.advancedrocketry.addtorch.exists=%s is already in the torch list +commands.advancedrocketry.addtorch.success=%s added to the torch list + +commands.advancedrocketry.addsealant.usage=/advancedrocketry addSealant - Adds held block to the list of blocks that be used as airtight seal +commands.advancedrocketry.addsealant.invalid=Held block cannot be added to sealed block list +commands.advancedrocketry.addsealant.exists=%s is already in the sealed block list +commands.advancedrocketry.addsealant.success=%s added to the sealed block list + +commands.advancedrocketry.weather.usage=/advancedrocketry weather [duration in seconds] +commands.advancedrocketry.weather.invalid=Current dimension (%s) is not an Advanced Rocketry planet! + msg.crystalliser.gravityTooHigh=Gravity is not low enough! msg.observetory.scan.tooltip=Scans for new asteroids, consumes 100 distance data msg.observetory.scan.button=Scan! @@ -287,6 +387,26 @@ msg.observetory.text.composition=Composition msg.observetory.text.processdiscovery=Process discovery msg.observetory.text.observabledistance=Observable distance: msg.observetory.text.missionTime=Mission Time: +msg.observetory.text.time=Time: +msg.observetory.req.open=Observatory must be open (night, clear sky, sky access) or be in space! +msg.observetory.print.already=You already printed a chip for this asteroid! + +# Atmosphere detector GUI labels +msg.atmosphere.air=Normal Air +msg.atmosphere.pressurizedair=Pressurized Air +msg.atmosphere.lowo2=Low Oxygen +msg.atmosphere.vacuum=Vacuum +msg.atmosphere.highpressure=High Pressure +msg.atmosphere.superhighpressure=Super High Pressure +msg.atmosphere.veryhot=Very Hot +msg.atmosphere.superheated=Superheated +msg.atmosphere.noo2=No Oxygen (No O₂) +msg.atmosphere.highpressurenoo2=High Pressure (No O₂) +msg.atmosphere.superhighpressurenoo2=Super High Pressure (No O₂) +msg.atmosphere.veryhotnoo2=Very Hot (No O₂) +msg.atmosphere.superheatednooxygen=Superheated (No O₂) + + msg.tooltip.data=Data msg.tooltip.asteroidselection=Asteroid Selection msg.label.name=Name @@ -310,9 +430,31 @@ msg.spaceElevator.warning.anchored1=the station anchored! msg.spaceElevator.warning.unanchored=This elevator has no tether msg.spaceElevator.turnedOff=Elevator is turned off msg.fuelingStation.link=You program the linker with the fueling station at + +msg.monitoringStation.buttonLaunch=Launch! msg.monitoringStation.missionProgressNA=Mission Progress: N/A +msg.monitoringStation.missionNoActiveMission=No Active Missions.. +msg.monitoringStation.mission.type.gas=Gas Collection Mission +msg.monitoringStation.mission.type.ore=Asteroid Mining Mission +msg.monitoringStation.mission.target.default=Harvest: (pending) +msg.monitoringStation.mission.targetPrefix=Harvest: +msg.monitoringStation.mission.Asteroid.target.default=Asteroid: +msg.monitoringStation.mission.Asteroid.targetPrefix=Asteroid: +msg.monitoringStation.mission.asteroidIdPrefix=Type: +msg.monitoringStation.mission.plannedAmountPrefix=Amount: +msg.monitoringStation.mission.plannedAmountPending=Amount: (pending) +msg.monitoringStation.mission.asteroidType=Asteroid type: (shown on chip / mission) msg.monitoringStation.link=You program the linker with the monitoring station at -msg.monitoringStation.progress= Progress: +msg.monitoringStation.progress=ETA: +msg.monitoringStation.prelaunch=Initiating... +msg.monitoringStation.launching=Launching! +msg.monitoringStation.orbit=Reached orbit! +msg.monitoringStation.deorbiting=Returned from orbit! +msg.monitoringStation.landed=Landed +msg.monitoringStation.aborted=Aborted! +msg.monitoringStation.returningToDock=Returning to dock +msg.monitoringStation.noLinkedRocket=Not Linked to any Rocket! + msg.guidanceComputerHatch.loadingState=Loading State: msg.guidanceComputerHatch.ejectonlanding=Auto Eject Upon Landing msg.guidanceComputerHatch.ejectonsatlanding=Allow Ejection of Satellite Chips @@ -321,18 +463,25 @@ msg.guidanceComputerHatch.ejectonstationlanding=Allow Ejection of Station Chips msg.guidanceComputerHatch.link=You program the linker with the fluid loader at msg.fluidLoader.loadingState=Loading State: msg.fluidLoader.allowLoading=Allow Loading: -msg.fluidLoader.allowredstoneinput=Allow redstone input -msg.fluidLoader.allowredstoneoutput=Allow redstone output -msg.fluidLoader.none=None +msg.fluidLoader.allowredstoneinput=Redstone Input (Red) +msg.fluidLoader.allowredstoneoutput=Redstone Output (Blue) +msg.fluidLoader.none=Disabled (Green) msg.fluidLoader.link=You program the linker with the fluid loader at: msg.rocketLoader.loadingState=Loading State: msg.rocketLoader.allowLoading=Allow Loading: -msg.rocketLoader.allowredstoneinput=Allow redstone input -msg.rocketLoader.allowredstoneoutput=Allow redstone output -msg.rocketLoader.none=None +msg.rocketLoader.none=Disabled (Green) +msg.rocketLoader.allowredstoneoutput=Redstone Output (Blue) +msg.rocketLoader.allowredstoneinput=Redstone Input (Red) msg.rocketLoader.link=You program the linker with the rocket loader at: -msg.microwaverec.notgenerating=Generating 0 FE/t +advancedrocketry.sideselector.direction.bottom=Bottom +advancedrocketry.sideselector.direction.top=Top +advancedrocketry.sideselector.direction.north=North +advancedrocketry.sideselector.direction.south=South +advancedrocketry.sideselector.direction.west=West +advancedrocketry.sideselector.direction.east=East +msg.microwaverec.notgenerating=Generating 0 RF/t msg.microwaverec.generating=Generating +msg.abdp.research=Research msg.abdp.compositionresearch=Composition Research msg.abdp.distanceresearch=Distance Research msg.abdp.massresearch=Mass Research @@ -344,22 +493,40 @@ msg.terraformer.outofgas=Aborted: Ran out of gasses msg.terraformer.notrunning=Not running msg.terraformer.status=Status msg.terraformer.pressure=Pressure +msg.terraformingterminal.terraforming=Terraforming planet... +msg.terraformingterminal.powergen=Power generation: +msg.terraformingterminal.blockspertick=Blocks per tick: +msg.terraformingterminal.needredstone.line1=Provide redstone signal +msg.terraformingterminal.needredstone.line2=to start the process +msg.terraformingterminal.insertchip.line1=Place a Biome Changer Remote +msg.terraformingterminal.insertchip.line2=here to make the Satellite +msg.terraformingterminal.insertchip.line3=terraform the entire planet msg.biomescanner.gas=nyehhh, Gassy, ain't it? msg.biomescanner.star=If only my sensors had sunshades msg.gravitycontroller.radius=Radius: msg.gravitycontroller.targetgrav=Target Gravity: -msg.gravitycontroller.none=Unset -msg.gravitycontroller.activeset=Active: set -msg.gravitycontroller.activeadd=Active: add +msg.gravitycontroller.none=No Force +msg.gravitycontroller.activeadd=Add Force (combine directions) +msg.gravitycontroller.activeset=Add Force (combine directions) + lift msg.gravitycontroller.targetdir.1=Target-> msg.gravitycontroller.targetdir.2=Direction msg.railgun.transfermin=Min Transfer Size msg.spacelaser.reset=Reset +msg.spacelaser.notarget1=No target found! +msg.spacelaser.notarget2=Go down and survey the area! +msg.spacelaser.voidmining.line1=Mining the internals +msg.spacelaser.voidmining.line2=of the planet below +msg.spacelaser.voidcobble=Void Cobble +msg.spacelaser.voidcobble.on=Void Cobble: ON +msg.spacelaser.voidcobble.off=Void Cobble: OFF msg.satctrlcenter.toofar=Too Far msg.satctrlcenter.nolink=No Link... msg.satctrlcenter.info=Info: msg.satctrlcenter.destroysat=Destroy Satellite -msg.satctrlcenter.connect=Connect! +msg.satctrlcenter.connect=Download +msg.satctrlcenter.data=Data: +msg.satctrlcenter.power=Power gen.: +msg.satctrlcenter.autodl_hint=Automatic with Wireless Tranciever (Extract) msg.satbuilder.writesecondchip=Write to Secondary Chip msg.dockingport.target=Target Id msg.dockingport.me=My Id @@ -369,15 +536,16 @@ msg.stationaltctrl.tgtalt=Target Altitude: msg.stationaltctrl.alt=Altitude: msg.stationgravctrl.maxaltrate=Max Gravity Change Rate: msg.stationgravctrl.tgtalt=Target Gravity: -msg.stationgravctrl.alt=Artifical Gravity: +msg.stationgravctrl.alt=Artificial Gravity: msg.stationorientctrl.alt=Angular Velocity: msg.stationorientctrl.tgtalt=Target Ang Vel: +msg.station.anchored=§cAnchored! msg.warpmon.tab.warp=Warp Selection msg.warpmon.tab.data=Data msg.warpmon.tab.tracking=Planet Tracking msg.warpmon.selectplanet=Select Planet msg.warpmon.corestatus=Core Status: -msg.warpmon.anchored=Station is anchored! +msg.warpmon.anchored=Anchored! msg.warpmon.nowhere=Nowhere to go msg.warpmon.missingart=Missing Artifact msg.warpmon.ready=Ready! @@ -386,6 +554,7 @@ msg.warpmon.warp=Warp! msg.warpmon.fuelcost=Fuel Cost: msg.warpmon.fuel=Fuel: msg.warpmon.dest=Dest: +msg.warpmon.orbit=Orbiting: msg.warpmon.na=N/A msg.warpmon.search=Search for planet msg.warpmon.chip=Program from chip @@ -394,10 +563,12 @@ msg.warpmon.artifact=Artifacts msg.rocketbuilder.success=Clear for liftoff! msg.rocketbuilder.nofuel=Not enough fuel capacity! msg.rocketbuilder.noseat=Missing seat or satellite bay! -msg.rocketbuilder.noengines=You do not have enough thrust! +msg.rocketbuilder.noengines=Not enough thrust! msg.rocketbuilder.noguidance=Missing Guidance Computer msg.rocketbuilder.unscanned=Rocket unscanned +msg.rocketbuilder.unscanned_station=Standing by for scan msg.rocketbuilder.success_station=Ready! +msg.rocketbuilder.fail_cut=Build failed: area changed msg.rocketbuilder.empty=Nothing here msg.rocketbuilder.finished=Build Complete! msg.rocketbuild.invalidblock=Invalid block! @@ -412,9 +583,13 @@ msg.rocketbuilder.acc=Acc msg.rocketbuilder.build=Build msg.rocketbuilder.scan=Scan msg.rocketbuild.combinedthrust=Fuel types cannot be combined! +msg.rocketbuilder.alreadyassembled=Rocket already assembled +msg.rocketbuilder.nointake=Missing Gas Intake! +msg.rocketbuilder.notank=Missing Fluidtank! msg.solar.collectingEnergy=Collecting Energy: msg.solar.cannotcollectEnergy=Unable to collect Energy msg.asteroidChip.asteroid=Asteroid +msg.asteroidChip.type=Type: msg.atmanal.atmtype=Atmosphere Type: msg.atmanal.canbreathe=Breathable: msg.biomechanger.scan=Scan Biome @@ -448,8 +623,11 @@ msg.itemsatellite.microwavestatus=Collecting Power msg.itemsatellite.data=Data Storage: msg.itemsatellite.nodata=No Data Storage! msg.itemsatellite.empty=Empty Chassis -msg.itemsatellite.weight=Chassis weight: +msg.itemsatellite.datagen=Data gen: %s/s +msg.itemsatellite.weight=Chassis weight: msg.itemsatellite.noweight=Error in weight calculation +msg.itemsatellite.unassembled=Not assembled (preview) + msg.brokenstage.text=Destruction stage @@ -473,26 +651,45 @@ msg.entity.rocket.launch=Launch in T- msg.entity.rocket.launch2=Press [Space] to abort msg.entity.rocket.station=Station msg.entity.rocket.pad=Pad: -msg.entity.rocket.disass=Dissassemble +msg.entity.rocket.disass=Disassemble msg.entity.rocket.seldst=Select Dst msg.entity.rocket.clear=Clear msg.entity.rocket.rcs=RCS Mode msg.entity.rocket.none=None Selected +msg.entity.rocket.openGuiHint=Press %s to open Rocket GUI msg.wirelessTransciever.extract=extract +msg.wirelessTransciever.insert=insert +msg.wirelessTransciever.type=Type: %s +msg.wirelessTransciever.network=Network: +msg.wirelessTransciever.network.unlinked=Unlinked + +msg.advancedrocketry.planetselector.up=<< Up +msg.advancedrocketry.planetselector.select=Select +msg.advancedrocketry.planetselector.planet.list=Planet List +msg.advancedrocketry.planetselector.atm.tooltip=%b -> %a Earth's atmospheric pressure +msg.advancedrocketry.planetselector.mass.tooltip=%b -> %a Earth's mass +msg.advancedrocketry.planetselector.distance.tooltip=%b -> %a Relative Distance units +msg.advancedrocketry.planetselector.star.tooltip.name=Name: %s +msg.advancedrocketry.planetselector.star.tooltip.number.of.planets=Number of Planets: %d +msg.advancedrocketry.planetselector.planet.tooltip.name=%s +msg.advancedrocketry.planetselector.planet.tooltip.moons.count=Moons: %d + -msg.powerunit.rfpertick=FE/t + +msg.powerunit.rfpertick=RF/t msg.linker.error.firstMachine=This must be the first machine to link! msg.linker.program=Coordinates programmed into Linker msg.linker.success=Linked Sucessfully -msg.notenoughpower=Not Enough power! +msg.linker.sameblock=You can't link a wireless transceiver to itself. +msg.notenoughpower=Not Enough Power! msg.empty=Empty msg.yes=yes msg.no=no -msg.connected=connected -msg.notconnected=not connected +msg.connected=Connected +msg.notconnected=Not Connected msg.unprogrammed=Unprogrammed msg.programfail=Programming Failed -msg.modules=modules +msg.modules=Modules: msg.na=N/A msg.entityDeployedRocket.notGasGiant=No Gas Here msg.noOxygen=Warning: Atmosphere lacks oxygen! @@ -504,6 +701,839 @@ msg.chat.nostation1=You wake up on the space station with a lingering feeling th msg.chat.nostation2=Maybe you should think before overstepping clearly logical and absolute boundaries again then deciding it was a good idea and not your fault if things go wrong msg.chat.nostation3=You must be on a space station to be in this dimension, and none have been created! +# Orbital Registry +msg.orbitalregistry.tab.satellites=Satellites: +msg.orbitalregistry.tab.stations=Space Stations: +msg.orbitalregistry.text.details=Details: +msg.orbitalregistry.text.satellites=Satellites +msg.orbitalregistry.text.stations=Space Stations +msg.orbitalregistry.text.nosel=Select an object +msg.orbitalregistry.text.notfound=Not found +msg.orbitalregistry.text.sat.datagen=Data gen: +msg.orbitalregistry.scan.tooltip=Update this list +msg.orbitalregistry.writechip.ok=Click to program chip! +msg.orbitalregistry.writechip.no=Cannot program this +msg.orbitalregistry.writechip=Program chip + + +# List entry +msg.orbitalregistry.text.listentry=ID + +# StationDetails +msg.orbitalregistry.text.type.starshiplist=§6Starship +msg.orbitalregistry.text.type.starship=Starship +msg.orbitalregistry.text.type.station=Station +msg.orbitalregistry.text.id=ID: +msg.orbitalregistry.text.type=Type: +msg.orbitalregistry.text.dimid=Dim: +msg.orbitalregistry.text.dimid.none=None +msg.orbitalregistry.text.orbit=Orbiting: +msg.orbitalregistry.text.orbit.unlaunched=Not in orbit! +msg.orbitalregistry.text.freepads=Free landingpads: +msg.orbitalregistry.text.anchored=Anchored: +msg.orbitalregistry.text.anchored.yes=Yes +msg.orbitalregistry.text.anchored.no=No +msg.orbitalregistry.text.system=System: +msg.orbitalregistry.text.system.none=None +msg.orbitalregistry.text.system.unknown=Unknown + +# SatelliteDetails power fields +msg.orbitalregistry.text.sat.pwrgen=Pwr Gen: +msg.orbitalregistry.text.sat.pwrstore=Pwr Store: +msg.orbitalregistry.text.sat.maxdata=Max Data: + +# Orbital Registry – satellite type names +msg.orbitalregistry.sat.name.optical=Telescope +msg.orbitalregistry.sat.name.density=Density +msg.orbitalregistry.sat.name.composition=Composition +msg.orbitalregistry.sat.name.mass=Mass +msg.orbitalregistry.sat.name.solarEnergy=Solar +msg.orbitalregistry.sat.name.oreScanner=Ore Scanner +msg.orbitalregistry.sat.name.biomeChanger=Biome Changer +msg.orbitalregistry.sat.name.weatherController=Weather + +msg.orbitalregistry.writechip.hint.insert=Insert a chip to write +msg.orbitalregistry.writechip.hint.select=Select an entry from the list +msg.orbitalregistry.writechip.hint.output=Clear the output slot first +msg.orbitalregistry.writechip.hint.sat.or.stationchip=Insert a Station Chip +msg.orbitalregistry.writechip.hint.sat.or.idchip=Insert a Satellite ID Chip or Controller +msg.orbitalregistry.writechip.hint.sat.badcontroller=This satellite does not accept this chip, use correct controller instead! +msg.orbitalregistry.writechip.hint.sat.orescanner.only=Ore Scanner can only link to Ore Mapping satellites +msg.orbitalregistry.writechip.hint.station.unlaunched=This station is not in orbit yet + + + commands.weather.always_not_clear=This planet is always not clear... commands.weather.cannot_rain=Cannot start a rain here commands.weather.cannot_thunder=Cannot start a thunder here + +# Jeistuff +jei.machinerecipe.power=Power: +jei.machinerecipe.time=Time: +jei.sb.satellitepreview=Ready for orbit! +jei.sb.copy.source=Source +jei.sb.copy.output=New Copy +jei.sb.assemblyhint=Atleast one solar panel +jei.sb.copychiphint=Make backups! +jei.ar.asteroids=Asteroids + + +jei.ar.fuel.role.monopropellant=Monopropellant Fuel +jei.ar.fuel.role.biprop_fuel=Bipropellant Fuel +jei.ar.fuel.role.oxidizer=Oxidizer +jei.ar.fuel.role.working_fluid=Working Fluid +jei.ar.stationAssembler.newStationChipHint=§cThis Points to the new Station! + +# TOP-integration + +msg.top.advancedrocketry.guidance.noComputer=No Guidance Computer +msg.top.advancedrocketry.guidance.noDestination=No Destination +msg.top.advancedrocketry.guidance.unprogrammed=Unprogrammed +msg.top.advancedrocketry.guidance.station=Station %1$s +msg.top.advancedrocketry.guidance.asteroidWithId=%1$s (%2$s) +msg.top.advancedrocketry.guidance.orbit=Orbit +msg.top.advancedrocketry.guidance.space=Space +msg.top.advancedrocketry.guidance.pad= / Pad %1$s + +msg.top.advancedrocketry.fuel.label=Fuel +msg.top.advancedrocketry.fuel.oxidizer=Oxidizer +msg.top.advancedrocketry.fuel.workingFluid=Working Fluid +msg.top.advancedrocketry.fuel.unknownFluid=Unknown fluid + +msg.top.advancedrocketry.rocket.monopropellant=Monopropellant Rocket +msg.top.advancedrocketry.rocket.bipropellant=Bipropellant Rocket +msg.top.advancedrocketry.rocket.nuclear=Nuclear Rocket +msg.top.advancedrocketry.modname=Advanced Rocketry + +msg.top.advancedrocketry.data.label=Data +msg.top.advancedrocketry.data.type=Type +msg.top.advancedrocketry.data.locked=Locked + +msg.top.advancedrocketry.data.mode=Mode +msg.top.advancedrocketry.data.mode.insert=Insert +msg.top.advancedrocketry.data.mode.extract=Extract +msg.top.advancedrocketry.data.link=Link +msg.top.advancedrocketry.data.link.linked=Linked +msg.top.advancedrocketry.data.link.unlinked=Unlinked + +######################### +##### TOOLTIP STUFF ##### +######################### +# Generic hint +tooltip.advancedrocketry.hold_shift=Hold §eShift§7 for details +tooltip.advancedrocketry.hold_alt=Hold §eAlt§7 for advanced tips + +# Fuel Tank (monoprop) +tooltip.advancedrocketry.fueltank=§cPart of Rocket +tooltip.advancedrocketry.fueltank.shift.1=§fHolds: §b%s +tooltip.advancedrocketry.fueltank.alt.1=Lets Rockety Rockrock! + +# Bipropellant Fuel Tank +tooltip.advancedrocketry.bipropfueltank=§cPart of Rocket +tooltip.advancedrocketry.bipropfueltank.shift.1=§fHolds: §b%s +tooltip.advancedrocketry.bipropfueltank.alt.1=§fBipropellant Rocket requires §bBipropellant§f and §bOxidizer§f tanks + +# Oxidizer Fuel Tank +tooltip.advancedrocketry.oxidizerfueltank=§cPart of Rocket +tooltip.advancedrocketry.oxidizerfueltank.shift.1=§fHolds: §b%s +tooltip.advancedrocketry.oxidizerfueltank.alt.1=§fBipropellant Rocket requires §bBipropellant§f and §bOxidizer§f tanks + +# Nuclear Fuel Tank +tooltip.advancedrocketry.nuclearfueltank=§cPart of Rocket +tooltip.advancedrocketry.nuclearfueltank.1=§6Allows planetary travel! +tooltip.advancedrocketry.nuclearfueltank.shift.1=§fHolds: §b%s +tooltip.advancedrocketry.nuclearfueltank.alt.1=§fRequires §bNuclear Core§f and §bNuclear Engine + +# Monopropellant Engine +tooltip.advancedrocketry.monopropmotor=§cPart of Rocket +tooltip.advancedrocketry.monopropmotor.shift.1=§fUses §bMonopropellant Fuel +tooltip.advancedrocketry.monopropmotor.alt.1=Check Fueling Station JEI-page + +# Nuclear Core +tooltip.advancedrocketry.nuclearcore=§cPart of Rocket +tooltip.advancedrocketry.nuclearcore.1=§6Allows planetary travel! +tooltip.advancedrocketry.nuclearcore.shift.1=Needs to be directly on top of a +tooltip.advancedrocketry.nuclearcore.shift.2=Nuclear Engine or another Nuclear Core. +tooltip.advancedrocketry.nuclearcore.alt.1=Vertical placement rules doesn't apply +tooltip.advancedrocketry.nuclearcore.alt.2=for Unmanned Vehicles (Gas Mission rockets) + +# Nuclear rocketengine +tooltip.advancedrocketry.nuclearmotor=§cPart of Rocket +tooltip.advancedrocketry.nuclearmotor.1=§6Allows planetary travel! +tooltip.advancedrocketry.nuclearmotor.shift.1=§fUses §bWorking fluid +tooltip.advancedrocketry.nuclearmotor.shift.2=Needs Nuclear Core directly above. +tooltip.advancedrocketry.nuclearmotor.alt.1=Check Fueling Station JEI-page + +# Bipropellant Engine +tooltip.advancedrocketry.bipropmotor=§cPart of Rocket +tooltip.advancedrocketry.bipropmotor.shift.1=§fUses §bBipropellant Fuel§f and §bOxidizer +tooltip.advancedrocketry.bipropmotor.alt.1=Check Fueling Station JEI-page + +# Drill +tooltip.advancedrocketry.drill=§cPart of Rocket +tooltip.advancedrocketry.drill.shift.1=Lowers duration of §6Mining Missions +tooltip.advancedrocketry.drill.shift.2=§bEffect stacks +tooltip.advancedrocketry.drill.alt.1=This rocket is built with Rocket Assembling Machine +tooltip.advancedrocketry.drill.alt.2=Requires a programmed Asteroid Chip + +# Gas Intake +tooltip.advancedrocketry.intake=§cPart of Rocket +tooltip.advancedrocketry.intake.shift.1=Lowers duration of §6Gas Missions +tooltip.advancedrocketry.intake.shift.2=§bEffect stacks +tooltip.advancedrocketry.intake.alt.1=This rocket is built with Unmanned Vehicle Assembler +tooltip.advancedrocketry.intake.alt.2=Launched from Space Station orbiting a Gas giant + +# Seat +tooltip.advancedrocketry.seat=§cPart of rocket +tooltip.advancedrocketry.seat.shift.1=Adds a passenger slot to the rocket + +# Guidance Computer +tooltip.advancedrocketry.guidancecomputer=§cPart of Rocket +tooltip.advancedrocketry.guidancecomputer.shift.1=Insert a §cChip§7 or programmed §cLinker§7 +tooltip.advancedrocketry.guidancecomputer.alt.1=Remember to tell rocket which planet to target +tooltip.advancedrocketry.guidancecomputer.alt.2=when deploying Satellites or Space Station into orbit + +# Service Monitor +tooltip.advancedrocketry.servicemonitor=§cPart of Rocket +tooltip.advancedrocketry.servicemonitor.shift.1=Enables damage view +tooltip.advancedrocketry.servicemonitor.shift.2=in rocket GUI +tooltip.advancedrocketry.servicemonitor.alt.1=WIP +tooltip.advancedrocketry.servicemonitor.alt.2= + +# Docking Pad (landingPad) +tooltip.advancedrocketry.landingpad=Replace the launchpad’s center block with this +tooltip.advancedrocketry.landingpad.shift.1=§cLinker§7 can store this block's exact position (dimension-aware). +tooltip.advancedrocketry.landingpad.shift.2=Insert in §4Guidance Computer§7 to land here. +tooltip.advancedrocketry.landingpad.alt.1=Place a programmed Linker in the Docking Pad. +tooltip.advancedrocketry.landingpad.alt.2=Rockets launched from this pad will fly to the Linker’s saved coordinates (used for automation). +tooltip.advancedrocketry.landingpad.alt.3=if there is nothing in the rocket's guidance computer. + +# Launch Pad +tooltip.advancedrocketry.launchpad=Base for Launch Pad platform. +tooltip.advancedrocketry.launchpad.shift.1=Place Launch Pad blocks in a flat square. +tooltip.advancedrocketry.launchpad.shift.2=3x3, 4x4, 5x5, .... +tooltip.advancedrocketry.launchpad.alt.1=§bAdd Structure Tower (minimum 4 high, starting at same y as Launch Pad) +tooltip.advancedrocketry.launchpad.alt.2=§fRocket/Station Assembler will connect automatically + +# Structure Tower +tooltip.advancedrocketry.structuretower=Vertical support for the Launch Pad platform. +tooltip.advancedrocketry.structuretower.shift.1=Tower must be minimum 4 blocks. +tooltip.advancedrocketry.structuretower.shift.2=Bottom block must touch the Launch Pad. +tooltip.advancedrocketry.structuretower.alt.1=Main block for Unmanned Vehicle Assembler structure +tooltip.advancedrocketry.structuretower.alt.2=Check wiki for more info. + +# Terraformer +tooltip.advancedrocketry.terraformer=Terraform the entire planet! +tooltip.advancedrocketry.terraformer.shift.1=§fUse with §cBiome Changer Satellite +tooltip.advancedrocketry.terraformer.shift.2=§fSatellite has to orbit this planet +tooltip.advancedrocketry.terraformer.alt.1=§fInsert §cBiome Changer Remote +tooltip.advancedrocketry.terraformer.alt.2=§fPowered by Satellite + +# Rocket Monitoring Station +tooltip.advancedrocketry.monitoringstation=§cInfrastructure +tooltip.advancedrocketry.monitoringstation.shift.1=Launch with Redstone Signal! +tooltip.advancedrocketry.monitoringstation.shift.2=§fLink to §4Rocket Assembler/Dockingpad§f or §4Rocket +tooltip.advancedrocketry.monitoringstation.alt.1=§bMissions becomes active when orbit reached! + +# Satellite Terminal +tooltip.advancedrocketry.satellitecontrolcenter=Communicates with Satellites +tooltip.advancedrocketry.satellitecontrolcenter.shift.1=Insert Satellite Chip +tooltip.advancedrocketry.satellitecontrolcenter.shift.2=Download Data +tooltip.advancedrocketry.satellitecontrolcenter.alt.1=§fTransfer and Auto-Download Data with Wireless Tranciever or Data Unit + +# Satellite Builder +tooltip.advancedrocketry.satellitebuilder=Assembles Satellites +tooltip.advancedrocketry.satellitebuilder.shift.1=Also Copy Chips/Remotes. +tooltip.advancedrocketry.satellitebuilder.shift.2=§bMust be on top of a Power Plug! +tooltip.advancedrocketry.satellitebuilder.alt.1=Insert chassis, Chip/Remote +tooltip.advancedrocketry.satellitebuilder.alt.2=core component + other components + +# Orbital Registry +tooltip.advancedrocketry.orbitalregistry=Tracks man-made objects in space +tooltip.advancedrocketry.orbitalregistry.shift.1=§fPrints new Chips! +tooltip.advancedrocketry.orbitalregistry.alt.1=§fTo destroy a Satellite use chip in §4Satellite Terminal + +### Infrastructure +## BlockARHatch + +# TileDataBusBig +tooltip.advancedrocketry.databusbig.header=§cBus and Unit +tooltip.advancedrocketry.databusbig.shift.1=§bCan only hold one type of Data +tooltip.advancedrocketry.databusbig.alt.1=§bKeeps data when broken, works as item and block + +# TileDataBus +tooltip.advancedrocketry.hatch.databus=Capacity: §62000 §7Data +tooltip.advancedrocketry.hatch.databus.shift.1=§bCan only hold one type of Data +tooltip.advancedrocketry.hatch.databus.alt.1=§fClears Data when broken + +# TileSatelliteHatch +tooltip.advancedrocketry.hatch.satellite=§cPart of Rocket +tooltip.advancedrocketry.hatch.satellite.shift.1=Used to put payloads in orbit +tooltip.advancedrocketry.hatch.satellite.shift.2=Remember to set target planet for orbit! +tooltip.advancedrocketry.hatch.satellite.alt.1=§fUsed in §4Station Assembler§f to pack and store the Station + +# TileRocketUnloader +tooltip.advancedrocketry.hatch.item_unloader=§cInfrastructure +tooltip.advancedrocketry.hatch.item_unloader.shift.1=Emits redstone when empty +tooltip.advancedrocketry.hatch.item_unloader.alt.1=§fLink to §4Rocket Assembler/Dockingpad§f or §4Rocket +tooltip.advancedrocketry.hatch.item_unloader.alt.2=§fRockets landing here will auto-link to this. + +# TileRocketLoader +tooltip.advancedrocketry.hatch.item_loader=§cInfrastructure +tooltip.advancedrocketry.hatch.item_loader.shift.1=Emits redstone when full +tooltip.advancedrocketry.hatch.item_loader.alt.1=§fLink to §4Rocket Assembler/Dockingpad§f or §4Rocket +tooltip.advancedrocketry.hatch.item_loader.alt.2=§fRockets landing here will auto-link to this. + +# TileRocketFluidUnloader +tooltip.advancedrocketry.hatch.fluid_unloader=§cInfrastructure +tooltip.advancedrocketry.hatch.fluid_unloader.shift.1=Emits redstone when empty +tooltip.advancedrocketry.hatch.fluid_unloader.alt.1=§fLink to §4Rocket Assembler/Dockingpad§f or §4Rocket +tooltip.advancedrocketry.hatch.fluid_unloader.alt.2=§fRockets landing here will auto-link to this. + +# TileRocketFluidLoader +tooltip.advancedrocketry.hatch.fluid_loader=§cInfrastructure +tooltip.advancedrocketry.hatch.fluid_loader.shift.1=Emits redstone when full +tooltip.advancedrocketry.hatch.fluid_loader.alt.1=§fLink to §4Rocket Assembler/Dockingpad§f or §4Rocket +tooltip.advancedrocketry.hatch.fluid_loader.alt.2=§fRockets landing here will auto-link to this. + +# Guidance Computer Access +tooltip.advancedrocketry.hatch.gca=§cInfrastructure +tooltip.advancedrocketry.hatch.gca.shift.1=Emits redstone when empty +tooltip.advancedrocketry.hatch.gca.alt.1=§fLink to §4Rocket Assembler/Dockingpad§f or §4Rocket +tooltip.advancedrocketry.hatch.gca.alt.2=§fRockets landing here will auto-link to this. + +## /BlockARHatch + +# Fueling Station +tooltip.advancedrocketry.fuelingstation=§cInfrastructure +tooltip.advancedrocketry.fuelingstation.shift.1=Emits redstone when full +tooltip.advancedrocketry.fuelingstation.shift.2=if station has same fueltype. +tooltip.advancedrocketry.fuelingstation.alt.1=§fLink to §4Rocket Assembler/Dockingpad§f or §4Rocket +tooltip.advancedrocketry.fuelingstation.alt.2=§fRockets landing here will auto-link to this. + +# Service Station +tooltip.advancedrocketry.servicestation=§cInfrastructure +tooltip.advancedrocketry.servicestation.shift.1=Repair rocket +tooltip.advancedrocketry.servicestation.shift.2=§o(WIP) + +## // Infrastructure + +# Pressurized Fluid Tank +tooltip.advancedrocketry.fluidtank.empty=Empty +tooltip.advancedrocketry.fluidtank.fluid=Fluid: +tooltip.advancedrocketry.fluidtank.level=Level: +tooltip.advancedrocketry.fluidtank.shift.1=§cConnects to tanks above or below to make a bigger tank + +# Wireless Transciever +tooltip.advancedrocketry.transceiver=Transfers §6Data +tooltip.advancedrocketry.transceiver.shift.1=§fUse §cLinker§f to create Networks +tooltip.advancedrocketry.transceiver.shift.2=§fSupports multiple Tranceivers +tooltip.advancedrocketry.transceiver.alt.1=§fExtractmode toggles autodownload from Terminal +tooltip.advancedrocketry.transceiver.alt.2=§o(reinsert chip if stale) + +# Atmosphere Detector +tooltip.advancedrocketry.atmosphereDetector=Emits redstone based on Atmosphere +tooltip.advancedrocketry.atmosphereDetector.shift.1=Select wanted Atmosphere +tooltip.advancedrocketry.atmosphereDetector.shift.2=emits if the condition is true +tooltip.advancedrocketry.atmosphereDetector.alt.1=Can detect: air, vacuum, +tooltip.advancedrocketry.atmosphereDetector.alt.2=low O₂, No O₂, very hot and more. + +# Station Light +tooltip.advancedrocketry.circlelight=Always on +tooltip.advancedrocketry.circlelight.shift.1=Does not require +tooltip.advancedrocketry.circlelight.shift.2=Power or Redstone + +# Gas Charge Pad +tooltip.advancedrocketry.oxygencharger=Charges Space Suit O₂ and H₂ +tooltip.advancedrocketry.oxygencharger.shift.1=Stand on pad to refill +tooltip.advancedrocketry.oxygencharger.alt.1=Does not require Power + +# Docking Port +tooltip.advancedrocketry.dockingport=Marks a station module docking point +tooltip.advancedrocketry.dockingport.shift.1=On the station: set a unique “My ID” +tooltip.advancedrocketry.dockingport.shift.2=On the new module: set “Target ID” +tooltip.advancedrocketry.dockingport.alt.1=Build new module in Station Builder +tooltip.advancedrocketry.dockingport.alt.2=clamp faces must face each other + +# Pipe Seal +tooltip.advancedrocketry.pipeseal=Airtight holes! +tooltip.advancedrocketry.pipeseal.shift.1=Seal a 1×1 gap by framing it with this +tooltip.advancedrocketry.pipeseal.shift.2=§bRequires 4 blocks per hole +tooltip.advancedrocketry.pipeseal.alt.1=Allows pipes through while keeping O₂ in +tooltip.advancedrocketry.pipeseal.alt.2=Entities can pass through the opening + +# Planet Selector (full-screen) +tooltip.advancedrocketry.planetselector=Browse Spacebodies +tooltip.advancedrocketry.planetselector.shift.1=Opens full-screen planet UI. +tooltip.advancedrocketry.planetselector.shift.2=Browse systems, planets and moons +tooltip.advancedrocketry.planetselector.alt.1=Allows you to remotely set a +tooltip.advancedrocketry.planetselector.alt.2=planet destination for warp controller. + +# Holographic Planet Selector +tooltip.advancedrocketry.planetholoselector=Holographic Spacebody Display +tooltip.advancedrocketry.planetholoselector.shift.1=Makes holographs in-world +tooltip.advancedrocketry.planetholoselector.alt.1="Functions the as planet selector +tooltip.advancedrocketry.planetholoselector.alt.2=but with a 3D holographic display" + +# Orientation Controller +tooltip.advancedrocketry.orientationctrl=§cSpace Station Controller +tooltip.advancedrocketry.orientationctrl.shift.1=Customize Angular Velocity +tooltip.advancedrocketry.orientationctrl.alt.1=§bCosmetic only§7 + +# Gravity Controller +tooltip.advancedrocketry.gravityctrl=§cSpace Station Controller +tooltip.advancedrocketry.gravityctrl.shift.1=Artificial gravity! +tooltip.advancedrocketry.gravityctrl.alt.1=Redstone control + +# Altitude Controller +tooltip.advancedrocketry.altitudectrl=§cSpace Station Controller +tooltip.advancedrocketry.altitudectrl.shift.1=Customize orbital height +tooltip.advancedrocketry.altitudectrl.alt.1=§bCosmetic only§7 +tooltip.advancedrocketry.altitudectrl.alt.2=Redstone control + +# Co2Scrubber +tooltip.advancedrocketry.scrubber=Place next to Oxygen Vent +tooltip.advancedrocketry.scrubber.shift.1=Reduces Oxygen use (max 2) +tooltip.advancedrocketry.scrubber.alt.1=Each scrubber halves O₂ use, increases Power use. +tooltip.advancedrocketry.scrubber.alt.2=With 2 Scrubbers Oxygen Vent won't use Oxygen. + +# Oxygen Vent +tooltip.advancedrocketry.oxygenvent=Creates breathable air in sealed rooms. +tooltip.advancedrocketry.oxygenvent.shift.1=Requires Power and Oxygen. +tooltip.advancedrocketry.oxygenvent.shift.2=Place inside enclosed area. +tooltip.advancedrocketry.oxygenvent.alt.1=Use CO2 Scrubber instead of oxygen. +tooltip.advancedrocketry.oxygenvent.alt.2=Range: %s blocks radius. + +# Airlock Door +tooltip.advancedrocketry.smallairlock=Airtight door! +tooltip.advancedrocketry.smallairlock.shift.1=Not airtight, leaks when open +tooltip.advancedrocketry.smallairlock.alt.1=Build a proper airlock +tooltip.advancedrocketry.smallairlock.alt.2=using 2 doors + +# Warp Controller +tooltip.advancedrocketry.warpcontroller=Turns the station into a §6Starship +tooltip.advancedrocketry.warpcontroller.shift.1=Place §4Warp Controller§7 + §4Warp Core§7 on a space station +tooltip.advancedrocketry.warpcontroller.shift.2=Warp between planets and solar systems +tooltip.advancedrocketry.warpcontroller.alt.1=UI shows location, destinations, and warp fuel +tooltip.advancedrocketry.warpcontroller.alt.2=(You made it this far! check wiki bro) + +# CarbonScrubberCartridge +tooltip.advancedrocketry.scrubbercart=Used in CO₂ Scrubber +tooltip.advancedrocketry.scrubbercart.shift.1=Purifies air at the cost of durability. +tooltip.advancedrocketry.scrubbercart.alt.1=Should last more than 24h + +# Seal Detector +tooltip.advancedrocketry.sealdetector=Detects if a block is airtight +tooltip.advancedrocketry.sealdetector.shift.1=§fRight Click on block to use + +# Lens +tooltip.advancedrocketry.lens=§cPart of Multiblock +tooltip.advancedrocketry.lens.shift.1=§bUse Holo-Projector! + +## SPACE SUIT + COMPONENTS + +# Suit Working Station +tooltip.advancedrocketry.suitworkingstation=Install/remove §5Space Suit Components +tooltip.advancedrocketry.suitworkingstation.shift.1=Works with Space Suit armor +tooltip.advancedrocketry.suitworkingstation.shift.2=(Helmet/Chest/Legs/Boots) +tooltip.advancedrocketry.suitworkingstation.alt.1=Does not require Power + +# Jetpack +tooltip.advancedrocketry.jetpack=§5Space Suit Component +tooltip.advancedrocketry.jetpack.shift.1=§dSlot: Chest§7 + +# AtmosphereAnalyzer +tooltip.advancedrocketry.atmanalyzer=§5Space Suit Component +tooltip.advancedrocketry.atmanalyzer.1=§fRight Click in hand to check atmosphere. +tooltip.advancedrocketry.atmanalyzer.shift.1=§dSlot: Helmet§7 +tooltip.advancedrocketry.atmanalyzer.alt.1=Breathable? Depends on atmosphere +tooltip.advancedrocketry.atmanalyzer.alt.2=Shows type and pressure + +# BeaconFinder +tooltip.advancedrocketry.beaconfinder=§5Space Suit Component +tooltip.advancedrocketry.beaconfinder.shift.1=§fShows a HUD arrow toward AR beacons in this dimension +tooltip.advancedrocketry.beaconfinder.shift.2=§dSlot: Helmet§7 +tooltip.advancedrocketry.beaconfinder.alt.1=Arrow offset is relative to your facing +tooltip.advancedrocketry.beaconfinder.alt.2=Works only in AR dimensions that have registered beacons. + +# PressureTank +tooltip.advancedrocketry.pressuretank.shift.1=§dSlot: Chest§7 +tooltip.advancedrocketry.pressuretank.alt.1=§fStores Oxygen for suit +tooltip.advancedrocketry.pressuretank.alt.2=§fStores Hydrogen for Jetpack + +## Item Upgrade +# 0 = Hover +tooltip.advancedrocketry.itemupgrade.0=§5Space Suit Component +tooltip.advancedrocketry.itemupgrade.0.shift.1=§fEnables Jetpack hover mode +tooltip.advancedrocketry.itemupgrade.0.shift.2=§dSlot: Helmet§7 +tooltip.advancedrocketry.itemupgrade.0.alt.1=Requires a Jetpack installed in the chestplate. +tooltip.advancedrocketry.itemupgrade.0.alt.2=§fSneak + Toggle Jetpack to activate + +# 1 = Flight Speed Control Upgrade +tooltip.advancedrocketry.itemupgrade.1=§5Space Suit Component +tooltip.advancedrocketry.itemupgrade.1.shift.1=§fBoosts Jetpack flight speed +tooltip.advancedrocketry.itemupgrade.1.shift.2=§dSlot: Leggings§7 +tooltip.advancedrocketry.itemupgrade.1.alt.1=Requires a Jetpack installed in the chestplate. +tooltip.advancedrocketry.itemupgrade.1.alt.2=§bEffect Stacks! + +# 2 = Bionic Leg Upgrade (speed) +tooltip.advancedrocketry.itemupgrade.2=§5Space Suit Component +tooltip.advancedrocketry.itemupgrade.2.shift.1=§fIncreases walk speed +tooltip.advancedrocketry.itemupgrade.2.shift.2=§dSlot: Leggings§7 +tooltip.advancedrocketry.itemupgrade.2.alt.1=Sprint to activate +tooltip.advancedrocketry.itemupgrade.2.alt.2=Stacks with multiple modules. + +# 3 = Padded Landing Boots Upgrade (no fall damage; config-aware) +tooltip.advancedrocketry.itemupgrade.3=§5Space Suit Component +tooltip.advancedrocketry.itemupgrade.3.shift.1=§fEliminates fall damage +tooltip.advancedrocketry.itemupgrade.3.shift.2=§dSlot: Feet§7 +tooltip.advancedrocketry.itemupgrade.3.alt.1=Stacking has no additional effect. + +# 4 = Antifog Visor Upgrade +tooltip.advancedrocketry.itemupgrade.4=§5Space Suit Component +tooltip.advancedrocketry.itemupgrade.4.shift.1=§fSee through fog on high pressure planets +tooltip.advancedrocketry.itemupgrade.4.shift.2=§dSlot: Helmet§7 +tooltip.advancedrocketry.itemupgrade.4.alt.1=Stacking has no additional effect. + +# 5 = Earthbright Visor +tooltip.advancedrocketry.itemupgrade.5=§5Space Suit Component +tooltip.advancedrocketry.itemupgrade.5.shift.1=§fAdjusts the lightlevels on distant worlds +tooltip.advancedrocketry.itemupgrade.5.shift.2=§dSlot: Helmet§7 +tooltip.advancedrocketry.itemupgrade.5.alt.1=Stacking has no additional effect. + +## Satellite Components +# Primary Function payloads +tooltip.advancedrocketry.satfunc.optical=§5Satellite Core Component +tooltip.advancedrocketry.satfunc.optical.shift.1=§bCollects Distance Data§7 +tooltip.advancedrocketry.satfunc.optical.shift.2=§oDownload Data in Satellite Terminal +tooltip.advancedrocketry.satfunc.optical.alt.1=Combine with Satellite Chip +tooltip.advancedrocketry.satfunc.optical.alt.2=when assembling + +tooltip.advancedrocketry.satfunc.composition=§5Satellite Core Component +tooltip.advancedrocketry.satfunc.composition.shift.1=§bCollects Composition Data§7 +tooltip.advancedrocketry.satfunc.composition.shift.2=§oDownload data in Satellite Terminal +tooltip.advancedrocketry.satfunc.composition.alt.1=Combine with Satellite Chip +tooltip.advancedrocketry.satfunc.composition.alt.2=when assembling + +tooltip.advancedrocketry.satfunc.mass=§5Satellite Core Component +tooltip.advancedrocketry.satfunc.mass.shift.1=§bCollects Mass Data§7 +tooltip.advancedrocketry.satfunc.mass.shift.2=§oDownload data in Satellite Terminal +tooltip.advancedrocketry.satfunc.mass.alt.1=Combine with Satellite Chip +tooltip.advancedrocketry.satfunc.mass.alt.2=when assembling + +tooltip.advancedrocketry.satfunc.microwave=§5Satellite Core Component +tooltip.advancedrocketry.satfunc.microwave.shift.1=§bGenerates Power in Space§7 +tooltip.advancedrocketry.satfunc.microwave.shift.2=§oNeeds Microwave Receiver (5x5 Multiblock) +tooltip.advancedrocketry.satfunc.microwave.alt.1=Combine with Satellite Chip +tooltip.advancedrocketry.satfunc.microwave.alt.2=when assembling + +tooltip.advancedrocketry.satfunc.oremapping=§5Satellite Core Component +tooltip.advancedrocketry.satfunc.oremapping.shift.1=§bScans the planet for Ore§7 +tooltip.advancedrocketry.satfunc.oremapping.alt.1=Combine with Ore Scanner +tooltip.advancedrocketry.satfunc.oremapping.alt.2=when assembling + +tooltip.advancedrocketry.satfunc.biomechanger=§5Satellite Core Component +tooltip.advancedrocketry.satfunc.biomechanger.shift.1=§bAdjusts biomes§7 +tooltip.advancedrocketry.satfunc.biomechanger.alt.1=Combine with Biome Changer Remote when assembling +tooltip.advancedrocketry.satfunc.biomechanger.alt.2=Requires Power generation and storage! + +tooltip.advancedrocketry.satfunc.weather=§5Satellite Core Component +tooltip.advancedrocketry.satfunc.weather.shift.1=§bDoes some weather stuff! +tooltip.advancedrocketry.satfunc.weather.alt.1=Combine with Weather Remote when assembling + +# Weather Remote +tooltip.advancedrocketry.weathercontrollerremote=§bShift-Right Click to open GUI +tooltip.advancedrocketry.weathercontrollerremote.shift.1=§fRight-click in hand to use +tooltip.advancedrocketry.weathercontrollerremote.alt.1=Combine with Weather Controller when assembling +tooltip.advancedrocketry.weathercontrollerremote.mode.rain=§eMode: Rain - Fills small basins in the terrain with water +tooltip.advancedrocketry.weathercontrollerremote.mode.dry=§eMode: Dry - Drys all water in a radius of 16 +tooltip.advancedrocketry.weathercontrollerremote.mode.flood=§eMode: Flood - Floods area with a radius of 16 with water + + +# Biome Changer Remote +tooltip.advancedrocketry.biomechangerremote=§bShift-Right Click to open GUI +tooltip.advancedrocketry.biomechangerremote.shift.1=§fRight-click in hand to transform a 20×20 area +tooltip.advancedrocketry.biomechangerremote.shift.2=§f"Scan Biome" stores the Biome you're standing in to satellite’s memory. +tooltip.advancedrocketry.biomechangerremote.alt.1=§6Satellite needs alot of power +tooltip.advancedrocketry.biomechangerremote.alt.2=§fUsed in §cTerraforming Terminal§f and§c Atmosphere Terraformer + +# Ore Scanner +tooltip.advancedrocketry.orescanner=§bRight-Click to open GUI +tooltip.advancedrocketry.orescanner.shift.1=§fIf satellite has at least §63,000 data§f storage, the Ore Scanner can filter by type. +tooltip.advancedrocketry.orescanner.alt.1=§fScan range depends on the satellite's energy generation + +# Power Sources +tooltip.advancedrocketry.satpower.0=§5Satellite Component +tooltip.advancedrocketry.satpower.0.shift.1=§fGenerates §c4 §fRF/t§7 +tooltip.advancedrocketry.satpower.0.shift.2=§oSatellites requires atleast 1 powergen + +tooltip.advancedrocketry.satpower.1=§5Satellite Component +tooltip.advancedrocketry.satpower.1.shift.1=§fGenerates §c40 §fRF/t§7 +tooltip.advancedrocketry.satpower.1.shift.2=§oSatellites requires atleast 1 powergen + +# LibVulpes Batteries +tooltip.libvulpes.battery.0=§5Satellite Component§7 +tooltip.libvulpes.battery.0.shift.1=Increases Powerstorage +tooltip.libvulpes.battery.0.shift.2=§fCapacity: §c10.000 §fRF§7 + +tooltip.libvulpes.battery.1=§5Satellite Component +tooltip.libvulpes.battery.1.shift.1=Increases Powerstorage +tooltip.libvulpes.battery.1.shift.2=§fCapacity: §c40.000 §fRF§7 + +# Data Unit +tooltip.advancedrocketry.itemdata.header=§5Satellite Component +tooltip.advancedrocketry.itemdata.type=§fType: +tooltip.advancedrocketry.itemdata.data=§fData stored: +tooltip.advancedrocketry.itemdataunit.shift.1=§fIncrease Satellite Data Storage by 1000 +tooltip.advancedrocketry.itemdataunit.alt.1=§fWorks as Datastorage in inventory aswell + +## Chips / remotes +# Asteroid Chip +tooltip.advancedrocketry.asteroidchip.shift.1=§bUsed for Mining Missions§7 +tooltip.advancedrocketry.asteroidchip.shift.2=§fProgram in §cObservatory +tooltip.advancedrocketry.asteroidchip.alt.1=§4Insert programmed Chip in Guidance Computer§7 +tooltip.advancedrocketry.asteroidchip.alt.2=§fYou get the chip back after Mission + +# Station Chip +tooltip.advancedrocketry.stationchip=§bMake Backups! +tooltip.advancedrocketry.stationchip.shift.1=§fProgram in Space Station Assembler +tooltip.advancedrocketry.stationchip.shift.2=§cCopied in Satellite Builder +tooltip.advancedrocketry.stationchip.alt.1=§4Insert programmed Chip in Guidance Computer§7 +tooltip.advancedrocketry.stationchip.namelabel=Name: + +# Planet Chip +tooltip.advancedrocketry.planetidchip=§bReprogammable! +tooltip.advancedrocketry.planetidchip.shift.1=§fInsert Chip in §4Guidance Computer§7 +tooltip.advancedrocketry.planetidchip.shift.2=§fSet destination in Rocket GUI to program it. +tooltip.advancedrocketry.planetidchip.alt.1=§4Doublecheck that this is programmed before Launch! + +# Satellite Chip +tooltip.advancedrocketry.satidchip=§bMake Backups! +tooltip.advancedrocketry.satidchip.shift.1=§fStores a satellite’s ID. +tooltip.advancedrocketry.satidchip.shift.2=§cCopied in Satellite Builder +tooltip.advancedrocketry.satidchip.alt.1=§4Use in Satellite Terminal to link or in the Microwave Receiver Multiblock (Input hatch). +tooltip.advancedrocketry.satidchip.alt.2=§8 (Planet: resolves if put in Terminal) + +# Elevator Chip +tooltip.advancedrocketry.elevatorchip=Space Elevator Chip +tooltip.advancedrocketry.elevatorchip.shift.1=§fLinks an elevator pad/destination. + +## Multiblocks +# Black Hole Generator +tooltip.advancedrocketry.blackholegen=Generates Power from compressed mass +tooltip.advancedrocketry.blackholegen.shift.1=§bUse Holo-Projector! + +# Microwave Receiver +tooltip.advancedrocketry.microwavereceiver=Receives Power from Solar Satellites +tooltip.advancedrocketry.microwavereceiver.shift.1=5x5 Multiblock +tooltip.advancedrocketry.microwavereceiver.shift.2=§bUse Holo-Projector! +tooltip.advancedrocketry.microwavereceiver.alt.1=§fSolar Satellites are built with §bMicrowave Transmitter§f and §bSatellite Chip +tooltip.advancedrocketry.microwavereceiver.alt.2=§8RF/t = (sum Satellites RF/t) * (2 * AtmospheredensityFactor) + +# Solar Panel (part of Microwave Receiver) +tooltip.advancedrocketry.solarpanel=§cPart of Multiblock +tooltip.advancedrocketry.solarpanel.shift.1=5x5 Multiblock +tooltip.advancedrocketry.solarpanel.shift.2=§o§f(Microwave Receiver) +tooltip.advancedrocketry.solarpanel.shift.3=§bUse Holo-Projector! + +# Solar Array Controller +tooltip.advancedrocketry.solararray=Generates Power from sunlight +tooltip.advancedrocketry.solararray.shift.1=Requires 63x Solar Array Panels +tooltip.advancedrocketry.solararray.shift.2=§bUse Holo-Projector! + +# Solar Array Panel +tooltip.advancedrocketry.solararraypanel=§cPart of Multiblock +tooltip.advancedrocketry.solararraypanel.shift.1=Requires 63x Solar Array Panels and Controller +tooltip.advancedrocketry.solararraypanel.shift.2=§bUse Holo-Projector! + +# Solar Generator +tooltip.advancedrocketry.solargenerator=Basic Solar Panel +tooltip.advancedrocketry.solargenerator.shift.1=§fGenerates §c2 §fRF/t§7 + +# Arc Furnace +tooltip.advancedrocketry.arcfurnace=Smelts at extreme temperatures +tooltip.advancedrocketry.arcfurnace.shift.1=§bUse Holo-Projector! + +# Rolling Machine +tooltip.advancedrocketry.rollingmachine=Rolls plates and foils +tooltip.advancedrocketry.rollingmachine.shift.1=§bUse Holo-Projector! + +# Lathe +tooltip.advancedrocketry.lathe=Turns rods and shafts +tooltip.advancedrocketry.lathe.shift.1=§bUse Holo-Projector! + +# Crystallizer +tooltip.advancedrocketry.crystallizer=Grows high-purity crystals +tooltip.advancedrocketry.crystallizer.shift.1=§bUse Holo-Projector! + +# Cutting Machine +tooltip.advancedrocketry.cuttingmachine=Precision cutting of materials +tooltip.advancedrocketry.cuttingmachine.shift.1=§bUse Holo-Projector! + +# Precision Assembler +tooltip.advancedrocketry.precisionassembler=Automates complex assembly +tooltip.advancedrocketry.precisionassembler.shift.1=§bUse Holo-Projector! + +# Electrolyser +tooltip.advancedrocketry.electrolyser=Splits compounds via electrolysis +tooltip.advancedrocketry.electrolyser.shift.1=§bUse Holo-Projector! + +# Chemical Reactor +tooltip.advancedrocketry.chemreactor=Processes chemical reactions +tooltip.advancedrocketry.chemreactor.shift.1=§bUse Holo-Projector! + +# Precision Laser Etcher +tooltip.advancedrocketry.precisionlaseretcher=Laser-etches fine circuits +tooltip.advancedrocketry.precisionlaseretcher.shift.1=§bUse Holo-Projector! + +# Observatory +tooltip.advancedrocketry.observatory=Analyzes celestial bodies +tooltip.advancedrocketry.observatory.shift.1=§fUsed for §6Mining Missions +tooltip.advancedrocketry.observatory.shift.2=§bUse Holo-Projector! +tooltip.advancedrocketry.observatory.alt.1=§fInsert §cAsteroid Chip +tooltip.advancedrocketry.observatory.alt.2=§fNeeds §6Distance Data§f to operate (operates only at night) + +# LibVulpes Hatches and Coal Generator +tooltip.advancedrocketry.libvulpes.hatch=§cPart of Multiblock +tooltip.advancedrocketry.libvulpes.hatch.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.libvulpes.forgepoweroutput=§cPart of Multiblock +tooltip.advancedrocketry.libvulpes.forgepoweroutput.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.libvulpes.forgepowerinput=§cPart of Multiblock +tooltip.advancedrocketry.libvulpes.forgepowerinput.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.libvulpes.creativepowerbattery=§dInfinite Power +tooltip.advancedrocketry.libvulpes.creativepowerbattery.shift.1=§cPart of Multiblock +tooltip.advancedrocketry.libvulpes.creativepowerbattery.shift.2=§bUse Holo-Projector! +tooltip.advancedrocketry.libvulpes.coalgenerator=§cBurns solid fuels + + +# Planet Analyser +tooltip.advancedrocketry.planetanalyser=Processeses data and writes it to Asteroid Chip +tooltip.advancedrocketry.planetanalyser.shift.1=§bUse Holo-Projector! + +# Centrifuge +tooltip.advancedrocketry.centrifuge=Separates by density +tooltip.advancedrocketry.centrifuge.shift.1=§bUse Holo-Projector! + +# Warp Core +tooltip.advancedrocketry.warpcore=Core for §6Starship +tooltip.advancedrocketry.warpcore.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.warpcore.alt.1=§6Starship §7needs §4Warp Controller§7 + §4Warp Core§7 + +# Beacon +tooltip.advancedrocketry.beacon=Long-range signal beacon +tooltip.advancedrocketry.beacon.shift.1=§bUse Holo-Projector! + +# Biome Scanner +tooltip.advancedrocketry.biomescan=Scans planetary biomes +tooltip.advancedrocketry.biomescan.shift.1=§bUse Holo-Projector! + +# Railgun +tooltip.advancedrocketry.railgun=Shoots Items into space +tooltip.advancedrocketry.railgun.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.railgun.alt.1="The railgun is not powerful enough to transport item stacks between planets and its range is limited to bodies within the same system" + +# Space Elevator Controller +tooltip.advancedrocketry.spaceelevatorctrl=Controls the Space Elevator +tooltip.advancedrocketry.spaceelevatorctrl.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.spaceelevatorctrl.shift.2=§fAnchors station +tooltip.advancedrocketry.spaceelevatorctrl.alt.1=§fNeeds air above (Planetside), air below (Stationside) +tooltip.advancedrocketry.spaceelevatorctrl.alt.2=§cUse Linker +# Atmosphere Terraformer +tooltip.advancedrocketry.atmosterraformer=Changes Atmospheric pressure on an entire planet +tooltip.advancedrocketry.atmosterraformer.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.atmosterraformer.alt.1=§fIncrease and decrease the Atmospheric pressure by using connected §cBiome Changer Remote. + +# Area Gravity Controller +tooltip.advancedrocketry.gravitymachine=Manipulates Gravity +tooltip.advancedrocketry.gravitymachine.shift.1=§fCan also affect the direction of Gravity +tooltip.advancedrocketry.gravitymachine.shift.2=§bUse Holo-Projector! + +# Orbital Last Drill +tooltip.advancedrocketry.spacelaser=§cSpace Station Multiblock +tooltip.advancedrocketry.spacelaser.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.spacelaser.alt.1=§fRequires Redstone to run + +# Force Field Projector +tooltip.advancedrocketry.forcefieldprojector=Projects up to 32 blocks away! +tooltip.advancedrocketry.forcefieldprojector.shift.1=§fActivate with Redstone + +# Vacuum Laser +tooltip.advancedrocketry.vacuumlaser=§cPart of Multiblock +tooltip.advancedrocketry.vacuumlaser.shift.1=§bUse Holo-Projector! + + +# Pump +tooltip.advancedrocketry.pump=Searches for fluid directly below +tooltip.advancedrocketry.pump.shift.1=§cPulls from the connected pool within 64 blocks. +tooltip.advancedrocketry.pump.alt.1=§fAutoejects to nearby tank +tooltip.advancedrocketry.pump.alt.2=Redstone turns it off + +# Parts for Multiblock +tooltip.advancedrocketry.concrete=§cPart of Multiblock +tooltip.advancedrocketry.concrete.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.blastbrick=§cPart of Multiblock +tooltip.advancedrocketry.blastbrick.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.qcrucible=§cPart of Multiblock +tooltip.advancedrocketry.qcrucible.shift.1=§bUse Holo-Projector! +tooltip.advancedrocketry.sawblade=§cPart of Multiblock +tooltip.advancedrocketry.sawblade.shift.1=§bUse Holo-Projector! +tooltip.libvulpes.structuremachine=§cPart of Multiblock +tooltip.libvulpes.structuremachine.shift.1=§bUse Holo-Projector! +tooltip.libvulpes.advstructuremachine=§cPart of Multiblock +tooltip.libvulpes.advstructuremachine.shift.1=§bUse Holo-Projector! + + +## Assemblers +# Rocket Assembler +tooltip.advancedrocketry.rocketassembler=§cBuilds Rockets +tooltip.advancedrocketry.rocketassembler.shift.1=§bRequires Launch Pad + Structure Tower Structure +tooltip.advancedrocketry.rocketassembler.shift.2=§fConnecting all infrastructure to this lets them autoconnect to Rocket on Pad +tooltip.advancedrocketry.rocketassembler.alt.1=§fPlace this 1 block higher than Launch Pad, connecting lower corners and facing opposite of the Launch Pad square + +# Station Assembler +tooltip.advancedrocketry.stationassembler=§cPacks Space Stations down for Launch! +tooltip.advancedrocketry.stationassembler.shift.1=§bRequires Launch Pad + Structure Tower Structure +tooltip.advancedrocketry.stationassembler.alt.1=§fPlace this 1 block higher than Launch Pad, connecting lower corners and facing opposite of the Launch Pad square + +# Packet Station +tooltip.advancedrocketry.packedstructure=Insert in §cSatellite Bay§7 to put in Orbit! +tooltip.advancedrocketry.packedstructure.shift.1=§eRemember to set the rocket's target planet before launch! + +# Deployable Rocket Assembler +tooltip.advancedrocketry.deployablerocketassembler=§cBuilds Rockets on Space Stations +tooltip.advancedrocketry.deployablerocketassembler.shift.1=§bRequires Structure Tower blocks +tooltip.advancedrocketry.deployablerocketassembler.shift.2=§fUsed for §6Gas Missions§7 +tooltip.advancedrocketry.deployablerocketassembler.alt.1=§fPlace this in the middle of upside down T facing outwards in space, then a horizontal support outwards from the top of tower. +tooltip.advancedrocketry.deployablerocketassembler.alt.2=Check wiki if unsure! + +# Hovercraft +tooltip.advancedrocketry.hovercraft=Long lasting dilithium Power source. It'll probably outlive you. +tooltip.advancedrocketry.hovercraft.alt.1=§fCheck steering in controls + +# Thermite +tooltip.advancedrocketry.thermite=Known for generating intense heat! + +# Thermite Torch +tooltip.advancedrocketry.thermitetorch=Burns without oxygen +tooltip.advancedrocketry.thermitetorch.shift.1=§fUsed in low or no Atmosphere + +# Jackhammer +tooltip.advancedrocketry.jackhammer=§cVery Fast Mining Tool +tooltip.advancedrocketry.jackhammer.1=It'll probably outlive you, (if you change bolts) +tooltip.advancedrocketry.jackhammer.shift.1=§fRepair with §6Titanium Rod +tooltip.advancedrocketry.jackhammer.alt.1=§fHarvestlevel: §bDiamond + +# Basic basicLaserGun +tooltip.advancedrocketry.lasergun=§cRanged Mining Tool +tooltip.advancedrocketry.lasergun.shift.1=§dInfinite uses +tooltip.advancedrocketry.lasergun.alt.1=§fHarvestlevel: §bDiamond +tooltip.advancedrocketry.lasergun.alt.2=§fRange: §b50 blocks + +## Crafting items +tooltip.advancedrocketry.sawbladeiron=§3Crafting Item +tooltip.advancedrocketry.wafer=§3Crafting Item +tooltip.advancedrocketry.circuitplate=§3Crafting Item +tooltip.advancedrocketry.circuitic=§3Crafting Item +tooltip.advancedrocketry.miscpart=§3Crafting Item +tooltip.advancedrocketry.itemlens=§3Crafting Item +tooltip.advancedrocketry.misc=§3Crafting Item diff --git a/src/main/resources/assets/advancedrocketry/lang/es_ES.lang b/src/main/resources/assets/advancedrocketry/lang/es_ES.lang index d17daaf60..dee6a00a0 100644 --- a/src/main/resources/assets/advancedrocketry/lang/es_ES.lang +++ b/src/main/resources/assets/advancedrocketry/lang/es_ES.lang @@ -108,6 +108,8 @@ item.jackhammer.name=Martillo neumático item.sealDetector.name=Detector de sellado tile.orientationControl.name=Controlador de orientación +msg.rocketbuilder.alreadyassembled=Cohete ya ensamblado + material.Dilithium.name=Dilitio @@ -131,4 +133,10 @@ fluid.oxygen=Oxígeno fluid.hydrogen=Hidrógeno fluid.rocketFuel=Combustible de cohete -mission.asteroidmining.name=Minería de asteroides \ No newline at end of file +mission.asteroidmining.name=Minería de asteroides + +jei.sb.satellitepreview=¡Listo para la órbita! +jei.sb.copy.source=Fuente +jei.sb.copy.output=Nueva copia +jei.sb.assemblyhint=Al menos un panel solar +jei.sb.copychiphint=¡Haz copias de seguridad! diff --git a/src/main/resources/assets/advancedrocketry/lang/fi_FI.lang b/src/main/resources/assets/advancedrocketry/lang/fi_FI.lang index f5270fc2a..4a741dd86 100644 --- a/src/main/resources/assets/advancedrocketry/lang/fi_FI.lang +++ b/src/main/resources/assets/advancedrocketry/lang/fi_FI.lang @@ -18,34 +18,34 @@ tile.seat.name=Istuin tile.pad.name=Laukaisualusta tile.structuretower.name=Rakennetorni tile.rocketAssembler.name=Raketin Kokoamislaite -tile.turf.name=Kuuply -tile.turfDark.name=Tumma Kuuply +tile.turf.name=Kuupöly +tile.turfDark.name=Tumma Kuupöly tile.cuttingMachine.name=Leikkuulaite -tile.sawBlade.name=Sahanter Kokoonpano -tile.controlComp.name=Tehtvn Ohjaustietokone +tile.sawBlade.name=Sahanterä Kokoonpano +tile.controlComp.name=Tehtävän Ohjaustietokone tile.precisionAssemblingMachine.name=Tarkkuuskokoaja tile.spaceLaser.name=Kiertorata Laser Pora -tile.Crystallizer.name=Kiteyttj -tile.blastBrick.name=Lmmnkestv tiili -tile.blastFurnaceController.name=Lmmnkestvn Uunin Ohjain +tile.Crystallizer.name=Kiteyttäjä +tile.blastBrick.name=Lämmönkestävä tiili +tile.blastFurnaceController.name=Lämmönkestävän Uunin Ohjain tile.fuelStation.name=Tankkausasema -tile.loader.0.name=Datavyl +tile.loader.0.name=Dataväylä tile.loader.1.name=Satelliittiasema tile.loader.2.name=Rakettipurkaja -tile.loader.3.name=Rakettityttj +tile.loader.3.name=Rakettitäyttäjä tile.loader.4.name=Rakettinestepurkaja -tile.loader.5.name=Rakettinestetyttj -tile.loader.6.name=Ohjaustietokoneen psyluukku +tile.loader.5.name=Rakettinestetäyttäjä +tile.loader.6.name=Ohjaustietokoneen pääsyluukku tile.observatory.name=Observatorio tile.satelliteBuilder.name=Satelliittirakentaja tile.rocket.name=Yksiajoaineinen Rakettimoottori tile.bipropellantrocket.name=Kaksiajoaineinen Rakettimoottori -tile.nuclearrocket.name=Ydinlamprakettimoottori +tile.nuclearrocket.name=Ydinlampörakettimoottori tile.fuelTank.name=Yksiajoainetankki tile.bipropellantfueltank.name=Kaksiajoainetankki tile.oxidizerfueltank.name=Hapetinpolttoainetankki -tile.nuclearfueltank.name=Ydinlmptystmisnestetankki -tile.nuclearcore.name=Ydinlmpfissioydin +tile.nuclearfueltank.name=Ydinlämpötyöstämisnestetankki +tile.nuclearcore.name=Ydinlämpöfissioydin tile.monitoringstation.name=Raketin Tarkkailuasema tile.satelliteMonitor.name=Satelliitti Terminaali tile.lightwoodlog.name=Valopuu @@ -56,7 +56,7 @@ tile.chipStorage.name=Satelliitti Id Varasto tile.planetanalyser.name=Avaruusobjekti Data Prosessori tile.lunaranalyser.name=Kuuanalysaattori tile.guidanceComputer.name=Ohjaustietokone -tile.electricArcFurnace.name=Shkinen Valokaariuuni +tile.electricArcFurnace.name=Sähköinen Valokaariuuni tile.hotDryturf.name=Hapetettu rautahiekka tile.concrete.name=Betoni tile.lathe.name=Sorvi @@ -78,35 +78,35 @@ tile.oxygenCharger.name=Kaasulataustyyny tile.dockingPad.name=Telakointialusta tile.stationmonitor.name=Hyperavaruusohjain tile.warpCore.name=Hyperavaruusydin -tile.atmosphereDetector.name=Ilmakehtunnistin +tile.atmosphereDetector.name=Ilmakehätunnistin tile.unlittorch.name=Sammunut Soihtu tile.geode.name=Geodikuutio -tile.electricMushroom.name=Shkinen Sieni +tile.electricMushroom.name=Sähköinen Sieni tile.charcoallog.name=Puuhiilitukki tile.vitrifiedSand.name=Lasittunut Hiekka tile.ruby.name=Punainen Kristallikuutio -tile.emerald.name=Vihre Kristallikuutio +tile.emerald.name=Vihreä Kristallikuutio tile.sapphire.name=Sininen Kristallikuutio tile.citrine.name=Keltainen Kristallikuutio tile.wulfentite.name=Oranssi Kristallikuutio tile.amethyst.name=Violetti Kristallikuutio tile.gravityControl.name=Avaruusaseman Painovoiman Ohjain tile.drill.name=Pora -tile.dataPipe.name=Datakaapeli(Poistettu kytst) -tile.liquidPipe.name=Nesteputki(Poistettu kytst) -tile.rfOutput.name=Redstone Flux Lhtpistoke +tile.dataPipe.name=Datakaapeli(Poistettu käytöstä) +tile.liquidPipe.name=Nesteputki(Poistettu käytöstä) +tile.rfOutput.name=Redstone Flux Lähtöpistoke tile.microwaveReciever.name=Mikroaaltovastaanotin tile.solarPanel.name=Aurinkopaneeli -tile.suitWorkStation.name=Pukutyasema +tile.suitWorkStation.name=Pukutyöasema tile.orientationControl.name=Suuntaohjain tile.biomeScanner.name=Biomiskanneri -tile.atmoshereTerraformer.name=Ilmakehterraformaattori -tile.deployableRocketAssembler.name=Miehittmttomn Kulkuneuvon Kokoaja +tile.atmoshereTerraformer.name=Ilmakehäterraformaattori +tile.deployableRocketAssembler.name=Miehittämättomän Kulkuneuvon Kokoaja tile.pressurizedTank.name=Paineistettu Tankki -tile.gasIntake.name=Kaasun Sisnotto -tile.atmosphereTerraformer.name=Ilmakehterraformaattori +tile.gasIntake.name=Kaasun Sisäänotto +tile.atmosphereTerraformer.name=Ilmakehäterraformaattori tile.circleLight.name=Asemavalo -tile.energyPipe.name=Shkputki(Poistettu kytst) +tile.energyPipe.name=Sähköputki(Poistettu käytöstä) tile.solarGenerator.name=Aurinkogeneraattori tile.stationMarker.name=Asematelakointiportti tile.qcrucible.name=Kvartsi Sulatusastia @@ -116,15 +116,15 @@ tile.advRocket.name=Edistynyt Yksiajoainerakettimoottori tile.advbipropellantRocket.name=Edistynyt Kaksiajoainerakettimoottori tile.planetHoloSelector.name=Holographinen Planeettavalitsin tile.lens.name=Linssi -tile.forceField.name=Voimakentt -tile.forceFieldProjector.name=Voimakenttprojektori -tile.vacuumLaser.name=Suuritehoinen Tyhjikammiolaser +tile.forceField.name=Voimakenttä +tile.forceFieldProjector.name=Voimakenttäprojektori +tile.vacuumLaser.name=Suuritehoinen Tyhjiökammiolaser tile.gravityMachine.name=Aluepainovoimaohjain tile.pipeSeal.name=Putkitiiviste tile.spaceElevatorController.name=Avaruushissi tile.beacon.name=Merkkivalo tile.thermiteTorch.name=Termiittisoihtu -tile.wirelessTransciever.name=Langaton Lhetin-Vastaanotin +tile.wirelessTransciever.name=Langaton Lähetin-Vastaanotin tile.blackholegenerator.name=Musta Aukko Generaattori tile.pump.name=Nestepumppu tile.centrifuge.name=Sentrifugi @@ -146,46 +146,46 @@ item.circuitIC.3.name=Ohjauspiiti item.circuitIC.4.name=Tavara IO Piiri item.circuitIC.5.name=Neste IO Piiri item.OreScanner.name=Malmiskanneri -item.dataUnit.0.name=Datatallennusyksikk -item.sawBlade.0.name=Rautainen Sahanter +item.dataUnit.0.name=Datatallennusyksikkö +item.sawBlade.0.name=Rautainen Sahanterä item.satellite.name=Satelliitti item.satellitePowerSource.0.name=Perusaurinkopaneeli item.satellitePowerSource.1.name=Iso Aurinkopaneeli item.satellitePrimaryFunction.0.name=Optinen Sensori item.satellitePrimaryFunction.1.name=Koostumussensori item.satellitePrimaryFunction.2.name=Massasensori -item.satellitePrimaryFunction.3.name=Mikroaaltolhetin +item.satellitePrimaryFunction.3.name=Mikroaaltolähetin item.satellitePrimaryFunction.4.name=Malmikartoittaja item.satellitePrimaryFunction.5.name=Biomimuuttaja item.satelliteIdChip.name=Satelliitti ID-Siru item.planetIdChip.name=Planeetta ID-Siru item.asteroidChip.name=Asteroidi ID-Siru -item.miscpart.0.name=Kyttliittym +item.miscpart.0.name=Käyttöliittymä item.miscpart.1.name=Hiilitiili item.station.name=Avaruusasemakontti item.stationChip.name=Avaruusasema ID-Siru -item.stationchip.openmenu=Kyyki ja klikkaa hiiren oikeaa nppint avataksesi asetusvalikon -item.spaceHelmet.name=Avaruuspukukypt +item.stationchip.openmenu=Kyyki ja klikkaa hiiren oikeaa näppäintä avataksesi asetusvalikon +item.spaceHelmet.name=Avaruuspukukypätä item.spaceChest.name=Avaruuspukupaita item.spaceLeggings.name=Avaruuspukuhousut -item.spaceBoots.name=Avaruuspukukengt +item.spaceBoots.name=Avaruuspukukengät item.smallAirlock.name=Pieni Ilmalukko-ovi -item.carbonScrubberCartridge.name=Hiilenkeryspatruuna +item.carbonScrubberCartridge.name=Hiilenkeräyspatruuna item.jackhammer.name=Nokkavasara item.sealDetector.name=Vuototunnistin -item.itemUpgrade.0.name=Leijumispivitys -item.itemUpgrade.1.name=Lentonopeuden Ohjauspivitys -item.itemUpgrade.2.name=Bioninen Jalkapivitys -item.itemUpgrade.3.name=Pehmustetut Laskeutumiskengt +item.itemUpgrade.0.name=Leijumispäivitys +item.itemUpgrade.1.name=Lentonopeuden Ohjauspäivitys +item.itemUpgrade.2.name=Bioninen Jalkapäivitys +item.itemUpgrade.3.name=Pehmustetut Laskeutumiskengät item.itemUpgrade.4.name=Sumunestovisiiri item.itemUpgrade.5.name=Maankirkas Visiiri -item.atmAnalyser.name=Ilmakehanalysaattori -item.biomeChanger.name=Biomin vaihtokaukosdin +item.atmAnalyser.name=Ilmakehäanalysaattori +item.biomeChanger.name=Biomin vaihtokaukosäädin item.basicLaserGun.name=Peruslaserpyssy -item.beaconFinder.name=Merkkivalon Lytj +item.beaconFinder.name=Merkkivalon Löytäjä item.thermite.name=Termiitti item.hovercraft.name=Leijualus -item.hovercraft.tooltip=Pitkn kestv dilitiumvoimanlhde. Se el todennkisesti pitempn kuin sin. +item.hovercraft.tooltip=Pitkään kestävä dilitiumvoimanlähde. Se elää todennäköisesti pitempään kuin sinä. item.jetPack.name=Asurakettireppu item.pressureTank.0.name=Matalapainetankki @@ -200,14 +200,14 @@ container.monitoringstation=Tarkkailuasema material.TitaniumAluminide.name=Titaanialuminidi material.TitaniumIridium.name=Titaani-Iridiumseos -enchantment.spaceBreathing=Ilmanpitv Tiiviste +enchantment.spaceBreathing=Ilmanpitävä Tiiviste data.undefined.name=Jotain Satunnaista Dataa -data.distance.name=Etisyys +data.distance.name=Etäisyys data.humidity.name=Ilmankosteus -data.temperature.name=Lmptila +data.temperature.name=Lämpötila data.composition.name=Koostumus -data.atmospheredensity.name=Ilmakehn Tiheys +data.atmospheredensity.name=Ilmakehän Tiheys data.mass.name=Massa fluid.oxygenHappi @@ -217,116 +217,116 @@ fluid.rocketFuel=Rakettipolttoaine fluid.enrichedLava=Rikastettu Laava mission.asteroidmining.name=Asteroidin Louhinta -mission.gascollection.name=Kaasun Kerys +mission.gascollection.name=Kaasun Keräys -error.rocket.cannotGetThere=Mrnp Tavoittamaton -error.rocket.destinationNotExist=Ei voi laukaista: Mrnp ei ole olemassa -error.rocket.notSameSystem=Ei voi laukaista: Mrnp ei ole samassa planeettasysteemiss +error.rocket.cannotGetThere=Määränpää Tavoittamaton +error.rocket.destinationNotExist=Ei voi laukaista: Määränpää ei ole olemassa +error.rocket.notSameSystem=Ei voi laukaista: Määränpää ei ole samassa planeettasysteemissä advancement.holographic=Holografinen advancement.holographic.desc=Rakenna Holo-Projektori -advancement.flattening=Litistv +advancement.flattening=Litistävä advancement.flattening.desc=Rakenna Pieni Levy Puristin -advancement.feelTheHeat=Tunne lmp! +advancement.feelTheHeat=Tunne lämpö! advancement.feelTheHeat.desc=Rakenna Valokaariuuni -advancement.electrifying=Shkistv! +advancement.electrifying=Sähköistävää! advancement.electrifying.desc=Rakenna Elekrolysaattori -advancement.spinDoctor=Pyrimistohtori +advancement.spinDoctor=Pyörimistohtori advancement.spinDoctor.desc=Rakenna Sorvi advancement.rollin=Rullaan advancement.rollin.desc=Rakenna Rullauskone advancement.crystalline=Kiteinen -advancement.crystalline.desc=Rakenna Kiteyttj +advancement.crystalline.desc=Rakenna Kiteyttäjä advancement.warp=Hyperavaruus advancement.warp.desc=Rakenna Hyperavaruusydin advancement.moonLanding=Kuulaskeutuminen! advancement.moonLanding.desc=Laskeudu Kuuhun advancement.oneSmallStep=Yksi Pieni Askel... -advancement.oneSmallStep.desc=Ole ensimminen joka laskeutuu kuuhun! +advancement.oneSmallStep.desc=Ole ensimmäinen joka laskeutuu kuuhun! advancement.weReallyWentToTheMoon=Me Oikeasti Menimme Kuuhun! -advancement.weReallyWentToTheMoon.desc=Lyd Apollo 11:n laskeutumispaikka Kuussa +advancement.weReallyWentToTheMoon.desc=Löydä Apollo 11:n laskeutumispaikka Kuussa advancement.dilithium=Dilitium -advancement.dilithium.desc=Lyd Dilitiummalmia -advancement.givingItAllShesGot=Annetaan kaikki mit hnell on! -advancement.givingItAllShesGot.desc=Lenn Hyperavaruuteen kykenevss aluksessa +advancement.dilithium.desc=Löydä Dilitiummalmia +advancement.givingItAllShesGot=Annetaan kaikki mitä hänellä on! +advancement.givingItAllShesGot.desc=Lennä Hyperavaruuteen kykenevässä aluksessa advancement.flightOfThePhoenix=Feeniksin Lento -advancement.flightOfThePhoenix.desc=Rakenna ja Lenn ensimmist Hyperavaruuteen kykenev alusta +advancement.flightOfThePhoenix.desc=Rakenna ja Lennä ensimmäistä Hyperavaruuteen kykenevää alusta advancement.beerOnTheSun=Aikuisten Juomia Auringossa -advancement.beerOnTheSun.desc=Tulet tarvitsemaan enemmn TNT pstksesi kiertoradalle +advancement.beerOnTheSun.desc=Tulet tarvitsemaan enemmän TNTä päästäksesi kiertoradalle advancement.suitedUp=Pukeutunut -advancement.suitedUp.desc=Pid yllsi kokonaista avaruuspukua +advancement.suitedUp.desc=Pidä ylläsi kokonaista avaruuspukua key.controls.advancedrocketry=Advanced Rocketry key.openRocketUI=Avaa Raketti GUI -key.toggleJetpack=Kynnist/Sammuta Rakettireppu -key.togglercs=Kynnist/Sammuta RCS -key.turnRocketLeft=Knn Kulkuneuvoa Vasemmalle -key.turnRocketRight=Knn Kulkuneuvoa Oikealle -key.turnRocketUp=Nosta Kulkuneuvoa Yls +key.toggleJetpack=Käynnistä/Sammuta Rakettireppu +key.togglercs=Käynnistä/Sammuta RCS +key.turnRocketLeft=Käännä Kulkuneuvoa Vasemmalle +key.turnRocketRight=Käännä Kulkuneuvoa Oikealle +key.turnRocketUp=Nosta Kulkuneuvoa Ylös key.turnRocketDown=Laske Kulkuneuvoa Alas -enchantment.advancedrocketry.spacebreathing.desc=Mahdollistaa haarniskan palan luomaan ilmanpitvn eristeen +enchantment.advancedrocketry.spacebreathing.desc=Mahdollistaa haarniskan palan luomaan ilmanpitävän eristeen machine.tooltip.smallplatepress=Tarvitsee obsidiaani kaksi kuutiota alapuolella toimiakseen msg.crystalliser.gravityTooHigh=Painovoima ei ole tarpeeksi alhainen! -msg.observetory.scan.tooltip=Skannaa uusia asteroideja, kytt 100 etisyys dataa +msg.observetory.scan.tooltip=Skannaa uusia asteroideja, käyttää 100 etäisyys dataa msg.observetory.scan.button=Skannaa! msg.observetory.text.asteroids=Asteroidit msg.observetory.text.composition=Koostumus -msg.observetory.text.processdiscovery=Prosessoi lyt -msg.observetory.text.observabledistance=Havaittava etisyys: -msg.observetory.text.missionTime=Tehtvaika: +msg.observetory.text.processdiscovery=Prosessoi löytö +msg.observetory.text.observabledistance=Havaittava etäisyys: +msg.observetory.text.missionTime=Tehtäväaika: msg.tooltip.data=Data msg.tooltip.asteroidselection=Asteroidivalikko msg.label.name=Nimi -msg.label.clear=Tyhjenn -msg.label.add=Lis Uusi -msg.label.rename=Nime Uudelleen +msg.label.clear=Tyhjennä +msg.label.add=Lisää Uusi +msg.label.rename=Nimeä Uudelleen msg.label.delete=Poista -msg.label.noneSelected=Ei Valittua Mrnpt -msg.label.selectDst=Valitse mrnp -msg.label.destName=Mrnpn nimi +msg.label.noneSelected=Ei Valittua Määränpäätä +msg.label.selectDst=Valitse määränpää +msg.label.destName=Määränpään nimi msg.label.coords=Koordinaatit msg.spaceElevator.button.summon=Luo Kapseli -msg.spaceElevator.sameDimensionError=Ei voi yhdist kahta hissi samalla planeetalla! +msg.spaceElevator.sameDimensionError=Ei voi yhdistää kahta hissiä samalla planeetalla! msg.spaceElevator.linkNotGeostationaryError=Asema ei ole geostationaarisella kiertoradalla! -msg.spaceElevator.tetherWouldBreakError=Aseman tytyy olla oikeinpin ja paikalleen vastaanottaakseen kaapelin! -msg.spaceElevator.linkCannotChangeError=Hissikaapelit eivt voi vaihtaa paikkaa kun ne ovat jo yhdistettyj! +msg.spaceElevator.tetherWouldBreakError=Aseman täytyy olla oikeinpäin ja paikalleen vastaanottaakseen kaapelin! +msg.spaceElevator.linkCannotChangeError=Hissikaapelit eivät voi vaihtaa paikkaa kun ne ovat jo yhdistettyjä! msg.spaceElevator.newDstAdded=Hissikaapeli Yhdistetty! msg.spaceElevator.ascentReady=Valmiina nousuun -msg.spaceElevator.warning.anchored0=Tll hissikaapelilla on +msg.spaceElevator.warning.anchored0=Tällä hissikaapelilla on msg.spaceElevator.warning.anchored1=asema ankkuroituna! -msg.spaceElevator.warning.unanchored=Tll hissill ei ole kaapelia -msg.spaceElevator.turnedOff=Hissi on poissa plt -msg.fuelingStation.link=Sin ohjelmoit yhdistjn tankkausaseman kanssa, joka on koordinaateissa -msg.monitoringStation.missionProgressNA=Tehtvn Edistys: N/A -msg.monitoringStation.link=Sin ohjelmoit yhdistjn tarkkailuaseman kanssa, joka on koordinaateissa +msg.spaceElevator.warning.unanchored=Tällä hissillä ei ole kaapelia +msg.spaceElevator.turnedOff=Hissi on poissa päältä +msg.fuelingStation.link=Sinä ohjelmoit yhdistäjän tankkausaseman kanssa, joka on koordinaateissa +msg.monitoringStation.missionProgressNA=Tehtävän Edistys: N/A +msg.monitoringStation.link=Sinä ohjelmoit yhdistäjän tarkkailuaseman kanssa, joka on koordinaateissa msg.monitoringStation.progress= Edistys: msg.guidanceComputerHatch.loadingState=Lastaustila: -msg.guidanceComputerHatch.ejectonlanding=Poista automaattisesti laskeutumisen jlkeen +msg.guidanceComputerHatch.ejectonlanding=Poista automaattisesti laskeutumisen jälkeen msg.guidanceComputerHatch.ejectonsatlanding=Salli Satelliittisirujen poistaminen msg.guidanceComputerHatch.ejectonplanetlanding=Salli Planeettasirujen poistaminen msg.guidanceComputerHatch.ejectonstationlanding=Salli Asemasirujen poistaminen -msg.guidanceComputerHatch.link=Sin ohjelmoit yhdistjn ohjaustietokoneen psyluukun kanssa, joka on koordinaateissa +msg.guidanceComputerHatch.link=Sinä ohjelmoit yhdistäjän ohjaustietokoneen pääsyluukun kanssa, joka on koordinaateissa msg.fluidLoader.loadingState=Lastaustila: msg.fluidLoader.allowLoading=Salli Lastaus: -msg.fluidLoader.allowredstoneinput=Salli redstone sisntulo +msg.fluidLoader.allowredstoneinput=Salli redstone sisääntulo msg.fluidLoader.allowredstoneoutput=Salli redstone ulosmeno -msg.fluidLoader.none=Ei mitn -msg.fluidLoader.link=Sin ohjelmoit yhdistjn nestelastaajan kanssa, joka on koordinaateissa +msg.fluidLoader.none=Ei mitään +msg.fluidLoader.link=Sinä ohjelmoit yhdistäjän nestelastaajan kanssa, joka on koordinaateissa msg.rocketLoader.loadingState=Lastaustila: msg.rocketLoader.allowLoading=Salli Lastaus: -msg.rocketLoader.allowredstoneinput=Salli redstone sisntulo +msg.rocketLoader.allowredstoneinput=Salli redstone sisääntulo msg.rocketLoader.allowredstoneoutput=Salli redstone ulosmeno -msg.rocketLoader.none=Ei mitn -msg.rocketLoader.link=Sin ohjelmoit yhdistjn rakettilastaajan kanssa, joka on koordinaateissa +msg.rocketLoader.none=Ei mitään +msg.rocketLoader.link=Sinä ohjelmoit yhdistäjän rakettilastaajan kanssa, joka on koordinaateissa msg.microwaverec.notgenerating=Tuottaa 0 FE/t msg.microwaverec.generating=Tuottaa msg.abdp.compositionresearch=Koostumustutkimus -msg.abdp.distanceresearch=Etisyystutkimus +msg.abdp.distanceresearch=Etäisyystutkimus msg.abdp.massresearch=Massatutkimus msg.terraformer.atminc=Nosta Ilmanpainetta msg.terraformer.atmdec=Laske Ilmanpainetta @@ -336,22 +336,22 @@ msg.terraformer.outofgas=Peruutettu: Kaasut loppuivat msg.terraformer.notrunning=Ei toimi msg.terraformer.status=Tila msg.terraformer.pressure=Paine -msg.biomescanner.gas=nyehhh, Kaasuista, eik? +msg.biomescanner.gas=nyehhh, Kaasuista, eikö? msg.biomescanner.star=Jos vain minun sensoreilla olisi aurinkolasit -msg.gravitycontroller.radius=Sde: +msg.gravitycontroller.radius=Säde: msg.gravitycontroller.targetgrav=Tavoitepainovoima: msg.gravitycontroller.none=Asettamaton msg.gravitycontroller.activeset=Aktiivinen: aseta -msg.gravitycontroller.activeadd=Aktiivinen: lis +msg.gravitycontroller.activeadd=Aktiivinen: lisää msg.gravitycontroller.targetdir.1=Kohde-> msg.gravitycontroller.targetdir.2=Suunta msg.railgun.transfermin=Minimi Siirtokoko msg.spacelaser.reset=Nollaa msg.satctrlcenter.toofar=Liian Kaukana -msg.satctrlcenter.nolink=Ei Linkki... +msg.satctrlcenter.nolink=Ei Linkkiä... msg.satctrlcenter.info=Info: msg.satctrlcenter.destroysat=Tuhoa Satelliitti -msg.satctrlcenter.connect=Yhdist! +msg.satctrlcenter.connect=Yhdistä! msg.satbuilder.writesecondchip=Kirjoita toiselle sirulle msg.dockingport.target=Kohde Id msg.dockingport.me=Minun Id @@ -370,113 +370,119 @@ msg.warpmon.tab.tracking=Planeettatarkkailu msg.warpmon.selectplanet=Valitse planeetta msg.warpmon.corestatus=Ytimen Tila: msg.warpmon.anchored=Asema on ankkuroitu! -msg.warpmon.nowhere=Ei ole minnekkn minne menn +msg.warpmon.nowhere=Ei ole minnekkään minne mennä msg.warpmon.missingart=Artefakti Puuttuu msg.warpmon.ready=Valmis! msg.warpmon.notready=Ei ole Valmis msg.warpmon.warp=Hyperavaruus! msg.warpmon.fuelcost=Polttoainekustannus: msg.warpmon.fuel=Polttoaine: -msg.warpmon.dest=Mrnp: +msg.warpmon.dest=Määränpää: msg.warpmon.na=N/A msg.warpmon.search=Etsi planeetta msg.warpmon.chip=Ohjelmoi sirusta -msg.warpmon.datareq=Tarvitaan 100 jokaista datatyyppi +msg.warpmon.datareq=Tarvitaan 100 jokaista datatyyppiä msg.warpmon.artifact=Artefaktit msg.rocketbuilder.success=Valmis laukaisuun! msg.rocketbuilder.nofuel=Ei tarpeeksi polttoainekapasiteettia! msg.rocketbuilder.noseat=Istuin tai satelliittiasema puuttuu! -msg.rocketbuilder.noengines=Sinulla ei ole tarpeeksi tyntvoimaa! +msg.rocketbuilder.noengines=Sinulla ei ole tarpeeksi työntövoimaa! msg.rocketbuilder.noguidance=Ohjaustietokone Puuttuu msg.rocketbuilder.unscanned=Raketti skannaamaton msg.rocketbuilder.success_station=Valmis! -msg.rocketbuilder.empty=Ei mitn tll +msg.rocketbuilder.empty=Ei mitään täällä msg.rocketbuilder.finished=Rakennus Valmis! -msg.rocketbuild.invalidblock=Ptemtn kuutio! +msg.rocketbuild.invalidblock=Pätemätön kuutio! msg.rocketbuilder.incompletestructure=Virheellinen Laukaisualustarakennus! msg.rocketbuilder.nosatellitehatch=Satelliittiasema Puuttuu msg.rocketbuilder.nosatellitechip=Siru Puuttuu msg.rocketbuilder.outputblocked=Ulostulo aukko tukittu -msg.rocketbuilder.thrust=Tyntvoima +msg.rocketbuilder.thrust=Työntövoima msg.rocketbuilder.weight=Paino msg.rocketbuilder.fuel=Polttoaine msg.rocketbuilder.acc=Kiihtyvyys msg.rocketbuilder.build=Rakenna msg.rocketbuilder.scan=Skannaa -msg.rocketbuild.combinedthrust=Polttoainetyyppej ei voi yhdist! -msg.solar.collectingEnergy=Energiankerys: -msg.solar.cannotcollectEnergy=Ei pysty kermn Energiaa +msg.rocketbuild.combinedthrust=Polttoainetyyppejä ei voi yhdistää! +msg.solar.collectingEnergy=Energiankeräys: +msg.solar.cannotcollectEnergy=Ei pysty keräämään Energiaa msg.asteroidChip.asteroid=Asteroidi -msg.atmanal.atmtype=Ilmakehtyyppi: -msg.atmanal.canbreathe=Hengitettv: +msg.atmanal.atmtype=Ilmakehätyyppi: +msg.atmanal.canbreathe=Hengitettävä: msg.biomechanger.scan=Skannaa Biomi -msg.biomechanger.nosat=Satelliitti ei ole viel laukaistu +msg.biomechanger.nosat=Satelliitti ei ole vielä laukaistu msg.biomechanger.selBiome=Valittu Biomi: -msg.biomechanger.numBiome=Skannattujen Biomien Mr: -msg.itemorescanner.nosat=Satelliitti ei ole viel laukaistu +msg.biomechanger.numBiome=Skannattujen Biomien Määrä: +msg.itemorescanner.nosat=Satelliitti ei ole vielä laukaistu msg.itemorescanner.maxzoom=Maksimi zoomaus: msg.itemorescanner.filter=Pystyy suodattamaan malmeja: msg.itemorescanner.value=Arvo: msg.itemplanetidchip.planetname=Planeetan Nimi: msg.itemplanetidchip.stationid=Asema Id: msg.itemplanetidchip.artifacts=Artefaktit: -msg.vent.trace=Happijlki +msg.vent.trace=Happijälki -msg.itemsatellite.pwr=Shkn Varastointi: -msg.itemsatellite.nopwr=Ei Shkn Varastointia -msg.itemsatellite.pwrgen=Shkn Tuotanto: -msg.itemsatellite.nopwrgen=Ei Shkn Tuotantoa! -msg.itemsatellite.microwavestatus=Shkn Kerys +msg.itemsatellite.pwr=Sähkön Varastointi: +msg.itemsatellite.nopwr=Ei Sähkön Varastointia +msg.itemsatellite.pwrgen=Sähkön Tuotanto: +msg.itemsatellite.nopwrgen=Ei Sähkön Tuotantoa! +msg.itemsatellite.microwavestatus=Sähkön Keräys msg.itemsatellite.data=Datan Varastointi: msg.itemsatellite.nodata=Ei Datan Varastointia! -msg.itemsatellite.empty=Tyhj Runko +msg.itemsatellite.empty=Tyhjä Runko msg.itemsatchip.id=ID: msg.itemsatchip.planet=Planeetta: msg.itemsatchip.planetunk=Planeetta: Tuntematon msg.itemsatchip.sat=Satelliitti: msg.itemsatchip.satlost=Satelliitti: Yhteys Menetetty -msg.sealdetector.sealed=Pitisi pit hyvn eristeen. -msg.sealdetector.notsealmat=Materiaali ei pid eristett. -msg.sealdetector.notsealblock=Kuutio ei pid eristett. -msg.sealdetector.notfullblock=Ilma kiert tmn kuution ymprilt. -msg.sealdetector.fluid=Ilma kuplii tmn kuution lpi. -msg.sealdetector.other=Ilma karkaa tmn kuution lpi. +msg.sealdetector.sealed=Pitäisi pitää hyvän eristeen. +msg.sealdetector.notsealmat=Materiaali ei pidä eristettä. +msg.sealdetector.notsealblock=Kuutio ei pidä eristettä. +msg.sealdetector.notfullblock=Ilma kiertää tämän kuution ympäriltä. +msg.sealdetector.fluid=Ilma kuplii tämän kuution läpi. +msg.sealdetector.other=Ilma karkaa tämän kuution läpi. msg.stationchip.sation=Asema -msg.entity.rocket.descend.1=Paina Vlilynti laskeutuaksesi! +msg.entity.rocket.descend.1=Paina Välilyöntiä laskeutuaksesi! msg.entity.rocket.descend.2=Automaattinen laskeutuminen -msg.entity.rocket.ascend.1=Paina Vlilynti laukaisemiseen! -msg.entity.rocket.ascend.2=Mrnp: +msg.entity.rocket.ascend.1=Paina Välilyöntiä laukaisemiseen! +msg.entity.rocket.ascend.2=Määränpää: msg.entity.rocket.launch=Laukaisu T- -msg.entity.rocket.launch2=Paina [Vlilynti] keskeyttksesi +msg.entity.rocket.launch2=Paina [Välilyönti] keskeyttääksesi msg.entity.rocket.station=Asema msg.entity.rocket.pad=Alusta: msg.entity.rocket.disass=Pura -msg.entity.rocket.seldst=Valitse Mrnp -msg.entity.rocket.clear=Tyhjenn +msg.entity.rocket.seldst=Valitse Määränpää +msg.entity.rocket.clear=Tyhjennä msg.entity.rocket.rcs=RCS Moodi msg.entity.rocket.none=Ei Valittu msg.wirelessTransciever.extract=ota talteen msg.powerunit.rfpertick=FE/t -msg.linker.error.firstMachine=Tmn tytyy olla ensimminen yhdistettv laite! -msg.linker.program=Koordinaatit ohjelmoitu yhdistjn +msg.linker.error.firstMachine=Tämän täytyy olla ensimmäinen yhdistettävä laite! +msg.linker.program=Koordinaatit ohjelmoitu yhdistäjään msg.linker.success=Onnistuneesti Yhdistetty -msg.notenoughpower=Ei Tarpeeksi shk! -msg.empty=Tyhj -msg.yes=kyll +msg.notenoughpower=Ei Tarpeeksi sähköä! +msg.empty=Tyhjä +msg.yes=kyllä msg.no=ei msg.connected=yhdistetty msg.notconnected=ei yhdistetty msg.unprogrammed=Ohjelmoimaton -msg.programfail=Ohjelmointi Eponnistui +msg.programfail=Ohjelmointi Epäonnistui msg.modules=moduulit msg.na=N/A -msg.entityDeployedRocket.notGasGiant=Ei Kaasua Tll -msg.noOxygen=Varoitus: Ilmakehss ei ole happea! -msg.tooHot=Varoitus: Ilmakeh liian kuuma! +msg.entityDeployedRocket.notGasGiant=Ei Kaasua Täällä +msg.noOxygen=Varoitus: Ilmakehässä ei ole happea! +msg.tooHot=Varoitus: Ilmakehä liian kuuma! msg.tooDense=Varoitus: Ilmanpaine liian korkea! msg.muchTooDense=Varoitus: Ilmanpaine kriittisen korkea! -msg.chat.nostation1=Sin hert avaruusasemalla viipyvn tunteen kanssa, ett sinun pitkkantoinen avaruuskvely oli jonkun vanhan kettujumalan paheksuma ja ett olisis typer yritt niin uudelleen ja odottaa erilaisia tuloksia -msg.chat.nostation2=Ehk sinun pitisi ajatella ennen kuin ylitt jlleen selkesti loogisia ja absoluuttisia rajoja ja sitten ptt, ett se oli hyv idea eik sinun vikasi, jos asiat menevt pieleen -msg.chat.nostation3=Sinun tytyy olla avaruusasemalla ollaksesi tss ulottuvuudessa, ja yhtn ei ole luotu! \ No newline at end of file +msg.chat.nostation1=Sinä heräät avaruusasemalla viipyvän tunteen kanssa, että sinun pitkäkantoinen avaruuskävely oli jonkun vanhan kettujumalan paheksuma ja että olisis typerää yrittää niin uudelleen ja odottaa erilaisia tuloksia +msg.chat.nostation2=Ehkä sinun pitäisi ajatella ennen kuin ylität jälleen selkeästi loogisia ja absoluuttisia rajoja ja sitten päättää, että se oli hyvä idea eikä sinun vikasi, jos asiat menevät pieleen +msg.chat.nostation3=Sinun täytyy olla avaruusasemalla ollaksesi tässä ulottuvuudessa, ja yhtään ei ole luotu! + +jei.sb.satellitepreview=Valmis kiertoradalle! +jei.sb.copy.source=Lähde +jei.sb.copy.output=Uusi kopio +jei.sb.assemblyhint=Vähintään yksi aurinkopaneeli +jei.sb.copychiphint=Tee varmuuskopiot! diff --git a/src/main/resources/assets/advancedrocketry/lang/fr_FR.lang b/src/main/resources/assets/advancedrocketry/lang/fr_FR.lang index f863c40f0..f06e2f69b 100644 --- a/src/main/resources/assets/advancedrocketry/lang/fr_FR.lang +++ b/src/main/resources/assets/advancedrocketry/lang/fr_FR.lang @@ -260,3 +260,10 @@ msg.spaceElevator.ascentReady=Paré pour l'ascension msg.spaceElevator.turnedOff=L'ascenseur est hors tension. msg.entityDeployedRocket.notGasGiant=Absence de gaz msg.noOxygen=Avertissement: oxygène non détecté! + +jei.sb.satellitepreview=Prêt pour l’orbite ! +jei.sb.copy.source=Source +jei.sb.copy.output=Nouvelle copie +jei.sb.assemblyhint=Au moins un panneau solaire +jei.sb.copychiphint=Faites des sauvegardes ! + diff --git a/src/main/resources/assets/advancedrocketry/lang/pt_br.lang b/src/main/resources/assets/advancedrocketry/lang/pt_br.lang index 3f63313c3..56020c67a 100644 --- a/src/main/resources/assets/advancedrocketry/lang/pt_br.lang +++ b/src/main/resources/assets/advancedrocketry/lang/pt_br.lang @@ -222,3 +222,11 @@ advancement.suitedUp.desc=Wear a full spacesuit key.controls.advancedrocketry=Advanced Rocketry key.openRocketUI=Open Rocket GUI key.toggleJetpack=Toggle Jetpack + + +jei.sb.satellitepreview=Pronto para a órbita! +jei.sb.copy.source=Fonte +jei.sb.copy.output=Nova cópia +jei.sb.assemblyhint=Pelo menos um painel solar +jei.sb.copychiphint=Faça backups! + diff --git a/src/main/resources/assets/advancedrocketry/lang/ru_RU.lang b/src/main/resources/assets/advancedrocketry/lang/ru_RU.lang index 9c25fc9d4..5d41eca70 100644 --- a/src/main/resources/assets/advancedrocketry/lang/ru_RU.lang +++ b/src/main/resources/assets/advancedrocketry/lang/ru_RU.lang @@ -376,6 +376,7 @@ msg.rocketbuilder.fuel=Топливо msg.rocketbuilder.acc=Точность msg.rocketbuilder.build=Стройка msg.rocketbuilder.scan=Сканирование +msg.rocketbuilder.alreadyassembled=Ракета уже собрана msg.solar.collectingEnergy=Сбор энергии: msg.solar.cannotcollectEnergy=Невозможно собрать энергию msg.asteroidChip.asteroid=Астероид @@ -448,3 +449,10 @@ msg.na=Н/Д commands.weather.always_not_clear=На этой планете не бывает сухо... commands.weather.cannot_rain=Невозможно начать дождь, увы... commands.weather.cannot_thunder=Невозможно включить грозу, увы... + + +jei.sb.satellitepreview=Готово к орбите! +jei.sb.copy.source=Источник +jei.sb.copy.output=Новая копия +jei.sb.assemblyhint=Как минимум одна солнечная панель +jei.sb.copychiphint=Делайте резервные копии! diff --git a/src/main/resources/assets/advancedrocketry/lang/ua_UA.lang b/src/main/resources/assets/advancedrocketry/lang/ua_UA.lang index 8c922dfd4..6321d6af0 100644 --- a/src/main/resources/assets/advancedrocketry/lang/ua_UA.lang +++ b/src/main/resources/assets/advancedrocketry/lang/ua_UA.lang @@ -159,3 +159,10 @@ mission.asteroidmining.name=Вивчення астероїдів key.controls.advancedrocketry=Advanced Rocketry key.toggleJetpack=Ввімкнути реактивний ранець + +jei.sb.satellitepreview=Готово до орбіти! +jei.sb.copy.source=Джерело +jei.sb.copy.output=Нова копія +jei.sb.assemblyhint=Щонайменше одна сонячна панель +jei.sb.copychiphint=Робіть резервні копії! + diff --git a/src/main/resources/assets/advancedrocketry/lang/zh_CN.lang b/src/main/resources/assets/advancedrocketry/lang/zh_CN.lang index 1f9c728f4..2923fcde6 100644 --- a/src/main/resources/assets/advancedrocketry/lang/zh_CN.lang +++ b/src/main/resources/assets/advancedrocketry/lang/zh_CN.lang @@ -1,79 +1,80 @@ itemGroup.advancedRocketry=高级火箭 -itemGroup.advancedRocketryOres=高级火箭矿物 - -death.attack.Vacuum=%1$s 因真空暴露死亡 -death.attack.Vacuum.player=%1$s 因真空暴露死亡 -death.attack.OxygenToxicity=%1$s 死于氧气中毒 -death.attack.OxygenToxicity.player=%1$s 死于氧气中毒 -death.attack.LowOxygen=%1$s 死于缺氧 -death.attack.LowOxygen.player=%1$s 死于缺氧 -death.attack.Heat=%1$s 死于高温暴露 -death.attack.Heat.player=%1$s 死于高温暴露 +itemGroup.advancedRocketryOres=高级火箭丨矿石 + +death.attack.Vacuum=%1$s因为失压而死 +death.attack.Vacuum.player=%1$s因为失压而死 +death.attack.OxygenToxicity=%1$s因氧气中毒而死 +death.attack.OxygenToxicity.player=%1$s因氧气中毒而死 +death.attack.LowOxygen=%1$s因缺氧而死 +death.attack.LowOxygen.player=%1$s因缺氧而死 +death.attack.Heat=%1$s因过热而死 +death.attack.Heat.player=%1$s因过热而死 entity.advancedRocketry.rocket.name=火箭 entity.rocket.name=火箭 +entity.deployedRocket.name=火箭 entity.hovercraft.name=气垫船 -tile.landingPad.name=着陆平台 +tile.landingPad.name=着陆台 tile.seat.name=座位 tile.pad.name=发射台 tile.servicestation.name=服务站 -tile.servicemonitor.name=服务监控器 -tile.invhatch.name=储物舱口 +tile.servicemonitor.name=服务监测器 +tile.invhatch.name=存储仓 tile.structuretower.name=结构塔 tile.rocketAssembler.name=火箭组装机 tile.turf.name=月面土 tile.turfDark.name=暗月面土 tile.cuttingMachine.name=切割机 -tile.sawBlade.name=电锯 -tile.controlComp.name=任务控制电脑 -tile.precisionAssemblingMachine.name=精确组装机 +tile.sawBlade.name=锯片组件 +tile.precisionAssemblingMachine.name=精密组装机 tile.spaceLaser.name=轨道激光钻 tile.Crystallizer.name=结晶器 tile.blastBrick.name=隔热砖 tile.blastFurnaceController.name=隔热高炉控制器 -tile.fuelStation.name=燃油站 +tile.fuelStation.name=加油站 +tile.databusbig.name=高级数据总线 tile.loader.0.name=数据总线 -tile.loader.1.name=卫星舱 +tile.loader.1.name=卫星仓 tile.loader.2.name=火箭卸载器 tile.loader.3.name=火箭装载器 -tile.loader.4.name=火箭液体卸载器 -tile.loader.5.name=火箭液体装载器 -tile.loader.6.name=导航电脑访问舱口 +tile.loader.4.name=火箭流体卸载器 +tile.loader.5.name=火箭流体装载器 +tile.loader.6.name=导航计算机访问仓 tile.observatory.name=瞭望台 -tile.satelliteBuilder.name=卫星建造机 -tile.rocket.name=单推进剂火箭发动机 -tile.bipropellantrocket.name=双燃料火箭发动机 +tile.satelliteBuilder.name=卫星组装机 +tile.rocket.name=单组元推进剂火箭发动机 +tile.bipropellantrocket.name=双组元推进剂火箭发动机 tile.nuclearrocket.name=核热火箭发动机 -tile.fuelTank.name=液体燃料箱 -tile.bipropellantfueltank.name=双燃料燃料箱 +tile.fuelTank.name=单组元推进剂燃料箱 +tile.bipropellantfueltank.name=双组元推进剂燃料箱 tile.oxidizerfueltank.name=氧化剂燃料箱 -tile.nuclearfueltank.name=核热工作燃料箱 -tile.nuclearcore.name=核热裂变核心 +tile.nuclearfueltank.name=核热工质箱 +tile.nuclearcore.name=核热裂变堆芯 tile.monitoringstation.name=火箭监测站 tile.satelliteMonitor.name=卫星终端 -tile.terraformingTerminal.name=地形改造终端 -tile.lightwoodlog.name=轻木原木 -tile.lightwoodsapling.name=轻木种子 -tile.lightwoodleaves.name=轻木叶子 +tile.terraformingTerminal.name=环境改造终端 +tile.lightwoodlog.name=轻树木 +tile.lightwoodsapling.name=轻树树苗 +tile.lightwoodleaves.name=轻树树叶 tile.lightwoodplanks.name=轻木木板 tile.chipStorage.name=卫星ID储存器 tile.planetanalyser.name=天体数据处理器 tile.lunaranalyser.name=月球分析器 -tile.guidanceComputer.name=导航电脑 +tile.guidanceComputer.name=导航计算机 tile.electricArcFurnace.name=电弧高炉 -tile.hotDryturf.name=氧化铁砂 +tile.hotDryturf.name=氧化铁沙 tile.concrete.name=混凝土 tile.lathe.name=车床 tile.rollingMachine.name=卷板机 tile.planetSelector.name=星球选择器 tile.blockHandPress.name=小型压板器 tile.placeHolder.name=机器 -tile.stationAssembler.name=太空站组装机 +tile.stationAssembler.name=空间站组装机 tile.electrolyser.name=电解器 tile.chemreactor.name=化学反应器 tile.scrubber.name=二氧化碳净化器 tile.oxygenVent.name=氧气排放口 -tile.liquidHatch.name=液体口 +tile.liquidHatch.name=流体仓 tile.rocketFuelBlock.name=火箭燃料 tile.hydrogenFluidBlock.name=氢气 tile.oxygenFluidBlock.name=氧气 @@ -83,73 +84,74 @@ tile.dockingPad.name=停靠台 tile.stationmonitor.name=跃迁控制器 tile.warpCore.name=跃迁核心 tile.atmosphereDetector.name=大气检测机 -tile.unlittorch.name=扑灭的火把 +tile.unlittorch.name=熄灭的火把 tile.geode.name=晶簇方块 tile.electricMushroom.name=电蘑菇 -tile.charcoallog.name=碳化树 +tile.charcoallog.name=碳化原木 tile.vitrifiedSand.name=玻璃化沙子 -tile.ruby.name=红宝石方块 -tile.emerald.name=绿宝石方块 -tile.sapphire.name=蓝宝石方块 +tile.ruby.name=红水晶方块 +tile.emerald.name=绿水晶方块 +tile.sapphire.name=蓝水晶方块 tile.citrine.name=黄水晶方块 -tile.wulfentite.name=钼铅矿方块 +tile.wulfentite.name=橙水晶方块 tile.amethyst.name=紫水晶方块 -tile.gravityControl.name=重力控制器 +tile.gravityControl.name=空间站重力控制器 tile.drill.name=钻头 -tile.dataPipe.name=数据线 -tile.liquidPipe.name=液体管道 +tile.dataPipe.name=数据线(弃用) +tile.liquidPipe.name=流体管道(弃用) tile.rfOutput.name=RF输出端口 tile.microwaveReciever.name=微波接收器 tile.solarPanel.name=太阳能板 tile.suitWorkStation.name=太空服调节台 tile.orientationControl.name=方向控制器 tile.biomeScanner.name=生物群系扫描仪 -tile.atmoshereTerraformer.name=大气修改器 -tile.deployableRocketAssembler.name=无人飞船组装器 -tile.pressurizedTank.name=压力槽 +tile.atmoshereTerraformer.name=大气改造器 +tile.deployableRocketAssembler.name=无人载具组装器 +tile.pressurizedTank.name=加压储罐 tile.gasIntake.name=气体收集器 tile.atmosphereTerraformer.name=大气改造器 tile.circleLight.name=空间站光源 -tile.energyPipe.name=能量管道 +tile.energyPipe.name=能量管道(弃用) tile.solarGenerator.name=太阳能发电机 -tile.stationMarker.name=空间站停靠点 +tile.stationMarker.name=空间站对接口 tile.qcrucible.name=石英坩埚 tile.altitudeController.name=海拔控制器 tile.railgun.name=轨道炮 -tile.advRocket.name=高级单推进剂火箭发动机 -tile.advbipropellantRocket.name=高级双推进剂火箭发动机 -tile.planetHoloSelector.name=全息行星选择器 +tile.advRocket.name=高级单组元推进剂火箭发动机 +tile.advbipropellantRocket.name=高级双组元推进剂火箭发动机 +tile.planetHoloSelector.name=全息星球选择器 tile.lens.name=透镜 -tile.forceField.name=力场方块 +tile.forceField.name=力场 tile.forceFieldProjector.name=力场投射器 tile.vacuumLaser.name=真空室高功率激光发射器 -tile.gravityMachine.name=重力控制器 +tile.gravityMachine.name=区域重力控制器 tile.pipeSeal.name=密封管 tile.spaceElevatorController.name=太空电梯 -tile.beacon.name=灯塔 -tile.thermiteTorch.name=铝热管 +tile.beacon.name=信标 +tile.thermiteTorch.name=铝热剂火把 tile.wirelessTransciever.name=无线收发器 -tile.blackholegenerator.name=黑洞发生器 +tile.blackholegenerator.name=黑洞发电机 tile.pump.name=流体泵 tile.centrifuge.name=离心机 -tile.precisionlaseretcher.name=精密激光刻蚀机 +tile.precisionlaseretcher.name=精密激光刻蚀器 tile.enrichedLavaBlock.name=浓缩熔岩块 tile.basalt.name=玄武岩 -tile.landingfloat.name=着陆浮标 -tile.solararray.name=太阳能电池 -tile.solararraypanel.name=太阳能电池板 +tile.landingfloat.name=着陆浮筒 +tile.solararray.name=太阳能阵列控制器 +tile.solararraypanel.name=阵列太阳能板 tile.serviceStation.name=服务站 +tile.orbitalRegistry.name=轨道注册站 item.lens.0.name=基础透镜 -item.wafer.0.name=硅晶片 -item.circuitplate.0.name=基础电路板 -item.circuitplate.1.name=高级电路板 -item.circuitIC.0.name=基础芯片 -item.circuitIC.1.name=跟踪芯片 -item.circuitIC.2.name=高级芯片 -item.circuitIC.3.name=控制芯片板 -item.circuitIC.4.name=物品IO芯片板 -item.circuitIC.5.name=液体IO芯片板 +item.wafer.0.name=硅晶圆 +item.circuitplate.0.name=基础电路基板 +item.circuitplate.1.name=高级电路基板 +item.circuitIC.0.name=基础电路 +item.circuitIC.1.name=跟踪电路 +item.circuitIC.2.name=高级电路 +item.circuitIC.3.name=控制电路板 +item.circuitIC.4.name=物品IO电路板 +item.circuitIC.5.name=流体IO电路板 item.OreScanner.name=矿物扫描仪 item.dataUnit.0.name=数据存储单元 item.sawBlade.0.name=铁锯片 @@ -160,63 +162,73 @@ item.satellitePrimaryFunction.0.name=光学传感器 item.satellitePrimaryFunction.1.name=成分传感器 item.satellitePrimaryFunction.2.name=质量检测器 item.satellitePrimaryFunction.3.name=微波传输器 -item.satellitePrimaryFunction.4.name=矿物扫描仪 -item.satellitePrimaryFunction.5.name=生物群落修改器 +item.satellitePrimaryFunction.4.name=矿物测绘器 +item.satellitePrimaryFunction.5.name=生物群系变换器 item.satellitePrimaryFunction.6.name=天气控制器 item.satelliteIdChip.name=卫星ID芯片 item.planetIdChip.name=星球ID芯片 -item.asteroidChip.name=小行星ID芯片 +item.asteroidChip.name=小行星芯片 item.miscpart.0.name=用户界面 item.miscpart.1.name=碳砖 item.station.name=空间站容器 item.stationChip.name=空间站ID芯片 -item.stationchip.openmenu=蹲下右键打开配置菜单 +item.stationchip.openmenu=潜行右击以打开配置菜单 item.spaceHelmet.name=太空头盔 item.spaceChest.name=太空胸甲 -item.spaceLeggings.name=太空裤子 -item.spaceBoots.name=太空鞋 +item.spaceLeggings.name=太空护腿 +item.spaceBoots.name=太空靴子 item.smallAirlock.name=小气密门 -item.carbonScrubberCartridge.name=碳收集器 +item.carbonScrubberCartridge.name=集炭滤芯 item.jackhammer.name=气锤 item.sealDetector.name=气密检测器 -item.itemUpgrade.0.name=盘旋升级 +item.itemUpgrade.0.name=悬停升级 item.itemUpgrade.1.name=飞行速度控制升级 item.itemUpgrade.2.name=仿生腿升级 item.itemUpgrade.3.name=降落缓冲鞋 -item.itemUpgrade.4.name=雾镜 -item.itemUpgrade.5.name=地球反光遮挡板 +item.itemUpgrade.4.name=防雾护目镜 +item.itemUpgrade.5.name=类地天光护目镜 item.atmAnalyser.name=大气分析器 -item.biomeChanger.name=生物群系改变器遥控终端 -item.weatherController.name=气象卫星控制器 +item.biomeChanger.name=生物群系变换器遥控终端 +item.weatherController.name=天气遥控终端 item.basicLaserGun.name=基础激光枪 -item.beaconFinder.name=灯塔探测器 +item.beaconFinder.name=信标定位器 item.thermite.name=铝热剂 item.hovercraft.name=气垫船 -item.hovercraft.tooltip=持久二锂电源,它可能会比你活得长 +item.satellite.opticaltelescope=光学望远镜 +item.satellite.composition=成分扫描器 +item.satellite.massscanner=质量扫描器 +item.satellite.solar=太阳能 +item.satellite.oremapper=矿物测绘器 +item.satellite.biomechanger=生物群系变换器 +item.satellite.weather=天气卫星 + item.jetPack.name=飞行背包 -item.pressureTank.0.name=低压槽 -item.pressureTank.1.name=压力槽 -item.pressureTank.2.name=高压槽 -item.pressureTank.3.name=超高压槽 +item.pressureTank.0.name=低压储罐 +item.pressureTank.1.name=气压储罐 +item.pressureTank.2.name=高压储罐 +item.pressureTank.3.name=超高压储罐 item.elevatorChip.name=太空电梯芯片 -container.satellite=卫星舱 +container.satellite=卫星仓 container.monitoringstation=监测站 -container.invhatch=储存舱口 +container.invhatch=存储仓 material.TitaniumAluminide.name=钛铝合金 material.TitaniumIridium.name=钛铱合金 -enchantment.spaceBreathing=严实的封口 +enchantment.spaceBreathing=气密密封 -data.undefined.name=一些随机数据 +data.undefined.name=未定义 data.distance.name=距离 data.humidity.name=湿度 data.temperature.name=温度 data.composition.name=成分 data.atmospheredensity.name=大气密度 data.mass.name=质量 +data.label.type=类型: +data.label.data=数据 + fluid.oxygen=氧气 fluid.hydrogen=氢气 @@ -225,285 +237,1268 @@ fluid.rocketFuel=火箭燃料 fluid.enrichedLava=浓缩熔岩 mission.asteroidmining.name=小行星采矿 -mission.gascollection.name=气体收集 +mission.gascollection.name=气体采集 -error.rocket.cannotGetThere=无法到达目标 -error.rocket.destinationNotExist=无法发射:目标不存在 -error.rocket.notSameSystem=无法发射:目标不在同一星球系统内 +error.rocket.notEnoughMissionFuel=燃料不足! +error.rocket.tooHeavy=火箭过重,无法发射(推力不足)。 +error.rocket.cannotGetThere=无法抵达所选目的地。(你在尝试登陆气态巨行星?) +error.rocket.destinationNotExist=所选空间站不存在。 +error.rocket.partsWornOut=关键部件损坏,发射中止。 +error.rocket.aborted=发射中止。 +error.rocket.gatedArtifactMissing=缺失工件。(玩家物品栏中) +error.rocket.gatedArtifactMissingWithItem=缺失所需工件:%sx %s(玩家物品栏中) +error.rocket.outsideStarSystem=星际航行需要星际飞船。 +error.rocket.outsidePlanetarySystem=行星航行需要核动力火箭。 advancement.holographic=全息 -advancement.holographic.desc=合成一个全息投射器 +advancement.holographic.desc=合成一个全息投影器 advancement.flattening=压扁 -advancement.flattening.desc=制作一个小压板机 -advancement.feelTheHeat=感受热量 +advancement.flattening.desc=制作一个小型压板器 +advancement.feelTheHeat=感受热量! advancement.feelTheHeat.desc=合成一个电弧高炉 -advancement.electrifying=电击 +advancement.electrifying=电激! advancement.electrifying.desc=合成一个电解器 -advancement.spinDoctor=旋转的医生 +advancement.spinDoctor=舆论导向专家 advancement.spinDoctor.desc=合成一个机床 -advancement.rollin=卷起来 +advancement.rollin=卷起来! advancement.rollin.desc=合成一个卷板机 -advancement.crystalline=晶体 +advancement.crystalline=结晶 advancement.crystalline.desc=合成一个结晶器 advancement.warp=跃迁 -advancement.warp.desc=制作一个跃迁核心 -advancement.moonLanding=登录月球 -advancement.moonLanding.desc=登录月球 -advancement.oneSmallStep=一小步 -advancement.oneSmallStep.desc=第一个到达月球 -advancement.weReallyWentToTheMoon=我们真的登上了月球! -advancement.weReallyWentToTheMoon.desc=找到月球上的阿波罗11着陆点 +advancement.warp.desc=建造一个跃迁核心 +advancement.moonLanding=登月! +advancement.moonLanding.desc=登陆月球 +advancement.oneSmallStep=一小步…… +advancement.oneSmallStep.desc=第一个到达月球! +advancement.weReallyWentToTheMoon=我们真的*去过*月球! +advancement.weReallyWentToTheMoon.desc=找到月球上的阿波罗11号着陆点 advancement.dilithium=双锂 -advancement.dilithium.desc=找到双锂矿 -advancement.givingItAllShesGot=全都在这里了 -advancement.givingItAllShesGot.desc=开上一艘可以跃迁的飞船 -advancement.flightOfThePhoenix=凤凰飞翔 -advancement.flightOfThePhoenix.desc=建造并起飞第一艘可以跃迁的飞船 +advancement.dilithium.desc=找到双锂矿石 +advancement.givingItAllShesGot=已经尽全力了! +advancement.givingItAllShesGot.desc=乘坐具有跃迁能力的飞船飞行 +advancement.flightOfThePhoenix=凤凰劫 +advancement.flightOfThePhoenix.desc=建造并起飞第一艘具备跃迁能力的飞船 advancement.beerOnTheSun=太阳上的成人饮料 -advancement.beerOnTheSun.desc=你需要更多的TNT才能到达轨道 -advancement.suitedUp=穿好了 +advancement.beerOnTheSun.desc=你需要更多的TNT才能进入轨道 +advancement.suitedUp=穿戴整齐 advancement.suitedUp.desc=穿好全套太空服 -key.controls.advancedrocketry=高级火箭 +key.controls.advancedrocketry=高级火箭(Advanced Rocketry) key.openRocketUI=打开火箭界面 -key.toggleJetpack=开关喷气背包 -key.togglercs=切换反作用控制系统 -key.turnRocketLeft=左转 -key.turnRocketRight=右转 -key.turnRocketUp=上移 -key.turnRocketDown=下移 +key.toggleJetpack=开关飞行背包 +key.togglercs=切换RCS(反作用控制系统) +key.turnRocketLeft=向左旋转载具 +key.turnRocketRight=向右旋转载具 +key.turnRocketUp=向上移动载具 +key.turnRocketDown=向下移动载具 + +enchantment.advancedrocketry.spacebreathing.desc=令盔甲部件形成气密密封 + +machine.tooltip.smallplatepress=需要下方两格处存在黑曜石才可工作 + +# Commands +commands.advancedrocketry.invalid=%s %s不存在! + +commands.advancedrocketry.dev.usage=/advancedrocketry dev help - (仅限开发者使用)列出子命令。 +commands.advancedrocketry.dev.dumpbiomes.usage=dumpBiomes - 将生物群系信息转存到BiomeDump.txt +commands.advancedrocketry.dev.dumpbiomes.success=文件'BiomeDump.txt'已写入到当前目录 +commands.advancedrocketry.dev.runtests.usage=runTests - 运行系统测试,仅用于调试! + +commands.advancedrocketry.filldata.usage=/advancedrocketry fillData <数据类型> <装填数量> +commands.advancedrocketry.filldata.invalid=非有效数据类型,请尝试以下类型: +commands.advancedrocketry.filldata.success=数据已装填! +commands.advancedrocketry.filldata.wrongtype=不匹配已存储的数据类型,未对数据做出任何更改 + +commands.advancedrocketry.goto.usage=/advancedrocketry goto help - 列出子命令。 +commands.advancedrocketry.goto.dimension.usage=goto dimension <维度ID> - 将玩家传送到指定维度 +commands.advancedrocketry.goto.station.usage=goto station <空间站ID> - 将玩家传送到指定空间站 + +commands.advancedrocketry.planet.usage=/advancedrocketry planet help - 列出子命令。 +commands.advancedrocketry.planet.reset.usage=planet reset [维度ID] +commands.advancedrocketry.planet.list.usage=planet list +commands.advancedrocketry.planet.list.dimensions=维度: +commands.advancedrocketry.planet.list.entry=维度%d:%s +commands.advancedrocketry.planet.delete.usage=planet delete <维度ID> +commands.advancedrocketry.planet.delete.success=维度%d已删除! +commands.advancedrocketry.planet.delete.invalid=目标世界中还有玩家存在: +commands.advancedrocketry.planet.generate.usage=planet generate <恒星ID:行星ID> [moon] [gas] <名称> <大气随机值> <距离随机值> <重力随机值> [大气基准值] [距离基准值] [重力基准值] +commands.advancedrocketry.planet.generate.invalid=维度%s生成失败! +commands.advancedrocketry.planet.generate.success=维度%s生成成功! +commands.advancedrocketry.planet.set.usage=planet set [维度ID] <属性名称> <属性值> +commands.advancedrocketry.planet.set.success=已成功将维度%d的%s属性设置为%s +commands.advancedrocketry.planet.set.invalid=属性查找失败,请检查日志 +commands.advancedrocketry.planet.set.mismatch=无法将类型为%2$s的%1$s属性设置为值%3$s +commands.advancedrocketry.planet.set.wronglength=数组属性长度为%d,但传入了%d个值 +commands.advancedrocketry.planet.get.usage=planet get [维度ID] <属性名称> <属性值> +commands.advancedrocketry.planet.get.success=%s=%s + +commands.advancedrocketry.star.usage=/advancedrocketry star help - 列出子命令。 +commands.advancedrocketry.star.action.temp.get=温度:%d +commands.advancedrocketry.star.action.temp.set=温度设置为:%d +commands.advancedrocketry.star.action.planets.get=绕该恒星运行的星球: +commands.advancedrocketry.star.action.planets.get.entry=ID:%d : %s +commands.advancedrocketry.star.action.pos.get=位置:%d, %d +commands.advancedrocketry.star.action.pos.set=位置设置为:%d, %d +commands.advancedrocketry.star.list.usage=star list +commands.advancedrocketry.star.list.entry=恒星ID:%d 名称:%s 星球数量:%d +commands.advancedrocketry.star.get.usage=star get <恒星ID> +commands.advancedrocketry.star.set.usage=star set <恒星ID> +commands.advancedrocketry.star.set.temp.usage=star set temp <恒星ID> <温度> +commands.advancedrocketry.star.set.pos.usage=star set pos <恒星ID> +commands.advancedrocketry.star.generate.usage=star generate <名称> <温度> +commands.advancedrocketry.star.generate.success=已添加恒星! +commands.advancedrocketry.star.generate.invalid=我怎么就装不下这么多恒星!(要么是你的恒星数量多得离谱,要么是真出大问题了!) -enchantment.advancedrocketry.spacebreathing.desc=可以让盔甲形成密封 +commands.advancedrocketry.station.usage=/advancedrocketry station help - 列出子命令。 +commands.advancedrocketry.station.create.usage=create <轨道维度ID> [玩家名称] [tp] - 创建一个绕<轨道维度ID>运行的新空间站,并生成一个3x3的圆石平台。若提供[玩家名称],将给予目标玩家一个对应的空间站ID芯片。若指定[tp]参数,则同时将该玩家传送至空间站。 +commands.advancedrocketry.station.create.invalid=维度ID %s 没有对应的高级火箭DimensionProperties +commands.advancedrocketry.station.create.tip=提示:/advancedrocketry planet list +commands.advancedrocketry.station.create.success=已创建绕维度%2$d运行,ID为%1$d的空间站(space @ %3$d, %4$d, %5$d) +commands.advancedrocketry.station.give.usage=give <空间站ID> [玩家名称] - 给予你(或指定<玩家名称>的玩家)一个ID为<空间站ID>的空间站 -machine.tooltip.smallplatepress=需要下面有两块黑曜石才能运作 +commands.advancedrocketry.fetch.usage=/advancedrocketry fetch <玩家名称> - 将指定<玩家名称>的玩家从任意维度传送到你的位置 -msg.crystalliser.gravityTooHigh=重力不够低 -msg.observetory.scan.tooltip=扫描新的小行星,将消耗100个距离数据 +commands.advancedrocketry.reloadrecipes.usage=/advancedrocketry reloadRecipes - 从config文件夹中的XML文件重载配方 +commands.advancedrocketry.reloadrecipes.error1=发生严重错误!可能导致配方损坏 +commands.advancedrocketry.reloadrecipes.error2=请检查日志! +commands.advancedrocketry.reloadrecipes.error3=你可以通过修复XML文件,或重启游戏来解决此错误 + +commands.advancedrocketry.setgravity.usage=/advancedrocketry setGravity <系数> [玩家名称] - 将你的重力(或指定[玩家名称]的玩家)设置为<系数>,其中1代表地球重力,0代表星球默认重力 + +commands.advancedrocketry.addtorch.usage=/advancedrocketry addTorch - 将当前手持的方块添加到无大气环境中会掉落的物体列表中 +commands.advancedrocketry.addtorch.invalid=手持方块无法添加到火把列表 +commands.advancedrocketry.addtorch.exists=%s早已在火把列表中了 +commands.advancedrocketry.addtorch.success=%s已添加到火把列表 + +commands.advancedrocketry.addsealant.usage=/advancedrocketry addSealant - 将当前手持的方块添加到可密封方块列表中 +commands.advancedrocketry.addsealant.invalid=手持方块无法添加到密封方块列表 +commands.advancedrocketry.addsealant.exists=%s早已在密封方块列表中了 +commands.advancedrocketry.addsealant.success=%s已添加到密封方块列表 + +commands.advancedrocketry.weather.usage=/advancedrocketry weather [持续时间(秒)] +commands.advancedrocketry.weather.invalid=当前维度(%s)不是高级火箭的星球! + +msg.crystalliser.gravityTooHigh=重力过高! +msg.observetory.scan.tooltip=扫描新的小行星,消耗100距离数据 msg.observetory.scan.button=扫描! msg.observetory.text.asteroids=小行星 msg.observetory.text.composition=成分 msg.observetory.text.processdiscovery=扫描进度 -msg.observetory.text.observabledistance=可观测距离: -msg.observetory.text.missionTime=任务时间: +msg.observetory.text.observabledistance=可观测距离: +msg.observetory.text.missionTime=任务时间: +msg.observetory.text.time=时间: +msg.observetory.req.open=瞭望台必须满足开放条件(夜晚、天气晴朗、可观测天空)或位于空间站中! +msg.observetory.print.already=你已为此小行星写入了一个芯片! + +# Atmosphere detector GUI labels +msg.atmosphere.air=正常空气 +msg.atmosphere.pressurizedair=加压空气 +msg.atmosphere.lowo2=低氧 +msg.atmosphere.vacuum=真空 +msg.atmosphere.highpressure=高压 +msg.atmosphere.superhighpressure=超高压 +msg.atmosphere.veryhot=高温 +msg.atmosphere.superheated=过热 +msg.atmosphere.noo2=无氧 +msg.atmosphere.highpressurenoo2=高压无氧 +msg.atmosphere.superhighpressurenoo2=超高压无氧 +msg.atmosphere.veryhotnoo2=高温无氧 +msg.atmosphere.superheatednooxygen=过热无氧 + + msg.tooltip.data=数据 msg.tooltip.asteroidselection=小行星选择 -msg.label.name=名字 -msg.label.clear=发射 -msg.label.add=添加新的 +msg.label.name=名称 +msg.label.clear=清除 +msg.label.add=新增 msg.label.rename=重命名 msg.label.delete=删除 msg.label.noneSelected=未选择目的地 msg.label.selectDst=选择目的地 msg.label.destName=目的地名称 msg.label.coords=坐标 -msg.spaceElevator.button.summon=调运舱体 -msg.spaceElevator.sameDimensionError=无法连接同一星球上的两部电梯! -msg.spaceElevator.linkNotGeostationaryError=空间站未处于地球静止轨道! -msg.spaceElevator.tetherWouldBreakError=空间站必须直立静止才能接收系链! -msg.spaceElevator.linkCannotChangeError=已连接的电梯系链不能改变位置! -msg.spaceElevator.newDstAdded=锚定缆索连接成功! -msg.spaceElevator.ascentReady=准备升空 -msg.spaceElevator.warning.anchored0=该电梯锚定缆索已 -msg.spaceElevator.warning.anchored1=锚定了空间站! -msg.spaceElevator.warning.unanchored=该电梯没有锚定缆索 +msg.spaceElevator.button.summon=呼唤太空舱 +msg.spaceElevator.sameDimensionError=无法在同一行星上连接两部电梯! +msg.spaceElevator.linkNotGeostationaryError=空间站不在同步轨道上! +msg.spaceElevator.tetherWouldBreakError=空间站必须保持垂直静止状态才能接收系绳! +msg.spaceElevator.linkCannotChangeError=电梯系绳在已链接状态下不可变更位置! +msg.spaceElevator.newDstAdded=电梯系绳已链接! +msg.spaceElevator.ascentReady=准备开始上升 +msg.spaceElevator.warning.anchored0=当前电梯系绳 +msg.spaceElevator.warning.anchored1=已成功锚定空间站! +msg.spaceElevator.warning.unanchored=当前电梯尚未连接系绳 msg.spaceElevator.turnedOff=电梯已关闭 -msg.fuelingStation.link=你将位于以下位置的燃油站对链接器进行编程 -msg.monitoringStation.missionProgressNA=任务进展: N/A -msg.monitoringStation.link=你将位于以下位置的监控站对链接器进行编程 -msg.monitoringStation.progress= 进度: -msg.guidanceComputerHatch.loadingState=加载状态: -msg.guidanceComputerHatch.ejectonlanding=着陆时自动弹射 -msg.guidanceComputerHatch.ejectonsatlanding=允许弹射卫星芯片 -msg.guidanceComputerHatch.ejectonplanetlanding=允许弹射行星芯片 -msg.guidanceComputerHatch.ejectonstationlanding=允许弹射空间站芯片 -msg.guidanceComputerHatch.link=你将位于以下位置的流体装载机对链接器进行编程 -msg.fluidLoader.loadingState=加载状态: -msg.fluidLoader.allowLoading=允许加载: -msg.fluidLoader.allowredstoneinput=允许红石输入 -msg.fluidLoader.allowredstoneoutput=允许红石输出 -msg.fluidLoader.none=无 -msg.fluidLoader.link=你将位于以下位置的流体装载机对链接器进行编程: -msg.rocketLoader.loadingState=载入状态: -msg.rocketLoader.allowLoading=允许加载: -msg.rocketLoader.allowredstoneinput=允许红石输入 -msg.rocketLoader.allowredstoneoutput=允许红石输出 -msg.rocketLoader.none=无 -msg.rocketLoader.link=你将位于以下位置的火箭加载器对链接器进行编程: -msg.microwaverec.notgenerating=生成 0 FE/t -msg.microwaverec.generating=正在生成 -msg.abdp.compositionresearch=合成研究 +msg.fuelingStation.link=已将加油站写入链接器,位置: + +msg.monitoringStation.buttonLaunch=发射! +msg.monitoringStation.missionProgressNA=任务进度:N/A +msg.monitoringStation.missionNoActiveMission=无激活任务…… +msg.monitoringStation.mission.type.gas=气体采集任务 +msg.monitoringStation.mission.type.ore=小行星采矿任务 +msg.monitoringStation.mission.target.default=收获:(待定) +msg.monitoringStation.mission.targetPrefix=收获: +msg.monitoringStation.mission.Asteroid.target.default=小行星: +msg.monitoringStation.mission.Asteroid.targetPrefix=小行星: +msg.monitoringStation.mission.asteroidIdPrefix=类型: +msg.monitoringStation.mission.plannedAmountPrefix=数量: +msg.monitoringStation.mission.plannedAmountPending=数量:(待定) +msg.monitoringStation.mission.asteroidType=小行星类型:(显示在芯片/任务上) +msg.monitoringStation.link=已将监控站写入链接器,位置: +msg.monitoringStation.progress=剩余时间: +msg.monitoringStation.prelaunch=启动中…… +msg.monitoringStation.launching=发射中! +msg.monitoringStation.orbit=已进入轨道! +msg.monitoringStation.deorbiting=已从轨道返回! +msg.monitoringStation.landed=已着陆 +msg.monitoringStation.aborted=已中止! +msg.monitoringStation.returningToDock=返回停靠点 +msg.monitoringStation.noLinkedRocket=未链接任何火箭! + +msg.guidanceComputerHatch.loadingState=装载状态: +msg.guidanceComputerHatch.ejectonlanding=着陆时自动弹出 +msg.guidanceComputerHatch.ejectonsatlanding=允许弹出卫星芯片 +msg.guidanceComputerHatch.ejectonplanetlanding=允许弹出星球芯片 +msg.guidanceComputerHatch.ejectonstationlanding=允许弹出空间站芯片 +msg.guidanceComputerHatch.link=已将流体装载器写入链接器,位置: +msg.fluidLoader.loadingState=装载状态: +msg.fluidLoader.allowLoading=允许装载: +msg.fluidLoader.allowredstoneinput=红石输入(红色) +msg.fluidLoader.allowredstoneoutput=红石输出(蓝色) +msg.fluidLoader.none=已禁用(绿色) +msg.fluidLoader.link=已将流体装载器写入链接器,位置: +msg.rocketLoader.loadingState=装载状态: +msg.rocketLoader.allowLoading=允许装载: +msg.rocketLoader.none=已禁用(绿色) +msg.rocketLoader.allowredstoneoutput=红石输出(蓝色) +msg.rocketLoader.allowredstoneinput=红石输入(红色) +msg.rocketLoader.link=已将火箭装载器写入链接器,位置: +advancedrocketry.sideselector.direction.bottom=底部 +advancedrocketry.sideselector.direction.top=顶部 +advancedrocketry.sideselector.direction.north=北部 +advancedrocketry.sideselector.direction.south=南部 +advancedrocketry.sideselector.direction.west=西部 +advancedrocketry.sideselector.direction.east=东部 +msg.microwaverec.notgenerating=产能 0 RF/t +msg.microwaverec.generating=产能 +msg.abdp.research=研究 +msg.abdp.compositionresearch=成分研究 msg.abdp.distanceresearch=距离研究 msg.abdp.massresearch=质量研究 msg.terraformer.atminc=增加大气压 msg.terraformer.atmdec=减少大气压 msg.terraformer.running=运行中 -msg.terraformer.missingbiome=缺少生物群落改变器链接 -msg.terraformer.outofgas=停止: 气体储备不足 +msg.terraformer.missingbiome=缺失生物群系变换器链接 +msg.terraformer.outofgas=已中止:气体耗尽 msg.terraformer.notrunning=未运行 msg.terraformer.status=状态 msg.terraformer.pressure=气压 -msg.biomescanner.gas=是啊,时髦,不是吗? -msg.biomescanner.star=如果我的传感器有遮阳帘就好了 -msg.gravitycontroller.radius=半径: -msg.gravitycontroller.targetgrav=目标重力: -msg.gravitycontroller.none=未设置 -msg.gravitycontroller.activeset=激活: 设置 -msg.gravitycontroller.activeadd=激活: 添加 -msg.gravitycontroller.targetdir.1=目标 -> +msg.terraformingterminal.terraforming=正在改造星球…… +msg.terraformingterminal.powergen=能量产出: +msg.terraformingterminal.blockspertick=每刻处理方块: +msg.terraformingterminal.needredstone.line1=提供红石信号 +msg.terraformingterminal.needredstone.line2=启动改造流程 +msg.terraformingterminal.insertchip.line1=在此处放入生物群系 +msg.terraformingterminal.insertchip.line2=变换器遥控终端来让 +msg.terraformingterminal.insertchip.line3=卫星改造整颗星球 +msg.biomescanner.gas=哈,是气体环境,对吧? +msg.biomescanner.star=要是我的传感器有遮阳板就好了 +msg.gravitycontroller.radius=半径: +msg.gravitycontroller.targetgrav=目标重力: +msg.gravitycontroller.none=无作用力 +msg.gravitycontroller.activeadd=添加作用力(合并各方向) +msg.gravitycontroller.activeset=添加作用力(合并各方向)+升力 +msg.gravitycontroller.targetdir.1=目标-> msg.gravitycontroller.targetdir.2=方向 -msg.railgun.transfermin=最小传送尺寸 +msg.railgun.transfermin=最小传输尺寸 msg.spacelaser.reset=重置 -msg.satctrlcenter.toofar=太远 -msg.satctrlcenter.nolink=无链接... -msg.satctrlcenter.info=信息: +msg.spacelaser.notarget1=未找到目标! +msg.spacelaser.notarget2=降落并勘察该区域! +msg.spacelaser.voidmining.line1=正在开采 +msg.spacelaser.voidmining.line2=下方星球的内部物质 +msg.spacelaser.voidcobble=销毁圆石 +msg.spacelaser.voidcobble.on=销毁圆石:开 +msg.spacelaser.voidcobble.off=销毁圆石:关 +msg.satctrlcenter.toofar=距离过远 +msg.satctrlcenter.nolink=无链接…… +msg.satctrlcenter.info=信息: msg.satctrlcenter.destroysat=摧毁卫星 -msg.satctrlcenter.connect=连接! +msg.satctrlcenter.connect=下载 +msg.satctrlcenter.data=数据: +msg.satctrlcenter.power=能量产出: +msg.satctrlcenter.autodl_hint=无线收发器自动下载(提取) msg.satbuilder.writesecondchip=写入第二芯片 -msg.dockingport.target=目标 Id -msg.dockingport.me=我的 Id +msg.dockingport.target=目标ID +msg.dockingport.me=本端ID msg.planetholo.size=全息图尺寸: -msg.stationaltctrl.maxaltrate=最大高度变化率: -msg.stationaltctrl.tgtalt=目标高度: -msg.stationaltctrl.alt=高度: -msg.stationgravctrl.maxaltrate=最大重力变化率: -msg.stationgravctrl.tgtalt=目标重力: -msg.stationgravctrl.alt=模拟重力: -msg.stationorientctrl.alt=角速度: -msg.stationorientctrl.tgtalt=目标角速度: +msg.stationaltctrl.maxaltrate=最大海拔变化率: +msg.stationaltctrl.tgtalt=目标海拔: +msg.stationaltctrl.alt=海拔: +msg.stationgravctrl.maxaltrate=最大重力变化率: +msg.stationgravctrl.tgtalt=目标重力: +msg.stationgravctrl.alt=人造重力: +msg.stationorientctrl.alt=角速度: +msg.stationorientctrl.tgtalt=目标角速度: +msg.station.anchored=§c已锚定! msg.warpmon.tab.warp=跃迁选择 msg.warpmon.tab.data=数据 -msg.warpmon.tab.tracking=行星跟踪 -msg.warpmon.selectplanet=选择行星 +msg.warpmon.tab.tracking=星球追踪 +msg.warpmon.selectplanet=选择星球 msg.warpmon.corestatus=核心状态: -msg.warpmon.anchored=空间站已锚定! -msg.warpmon.nowhere=无处可去 +msg.warpmon.anchored=已锚定! +msg.warpmon.nowhere=无可用目的地 msg.warpmon.missingart=缺少工件 -msg.warpmon.ready=准备就绪! +msg.warpmon.ready=就绪! msg.warpmon.notready=未就绪 -msg.warpmon.warp=跃迁! -msg.warpmon.fuelcost=燃料成本: -msg.warpmon.fuel=燃料: -msg.warpmon.dest=目的地: +msg.warpmon.warp=跃迁! +msg.warpmon.fuelcost=燃料消耗: +msg.warpmon.fuel=燃料: +msg.warpmon.dest=目的地: +msg.warpmon.orbit=轨道环绕: msg.warpmon.na=N/A -msg.warpmon.search=搜索行星 -msg.warpmon.chip=芯片编程 -msg.warpmon.datareq=每种数据类型需要100个 +msg.warpmon.search=搜索星球 +msg.warpmon.chip=从芯片载入程序 +msg.warpmon.datareq=需各类数据各100 msg.warpmon.artifact=工件 -msg.rocketbuilder.success=可以发射了! -msg.rocketbuilder.nofuel=燃料容量不够! -msg.rocketbuilder.noseat=缺少座位或卫星舱! -msg.rocketbuilder.noengines=你没有足够的推力! -msg.rocketbuilder.noguidance=缺少导航电脑 +msg.rocketbuilder.success=可以发射! +msg.rocketbuilder.nofuel=燃料容量不足! +msg.rocketbuilder.noseat=缺少座位或卫星仓! +msg.rocketbuilder.noengines=推力不足! +msg.rocketbuilder.noguidance=缺少导航计算机 msg.rocketbuilder.unscanned=火箭未扫描 -msg.rocketbuilder.success_station=准备就绪! -msg.rocketbuilder.empty=这里什么都没有 -msg.rocketbuilder.finished=建造完成! -msg.rocketbuild.invalidblock=无效块! -msg.rocketbuilder.incompletestructure=无效的发射台结构! -msg.rocketbuilder.nosatellitehatch=缺少卫星舱 +msg.rocketbuilder.unscanned_station=等待扫描 +msg.rocketbuilder.success_station=就绪! +msg.rocketbuilder.fail_cut=搭建失败:区域已变更 +msg.rocketbuilder.empty=区域为空 +msg.rocketbuilder.finished=搭建完成! +msg.rocketbuild.invalidblock=无效方块! +msg.rocketbuilder.incompletestructure=发射台结构无效! +msg.rocketbuilder.nosatellitehatch=缺少卫星仓 msg.rocketbuilder.nosatellitechip=缺少芯片 msg.rocketbuilder.outputblocked=输出槽被阻塞 msg.rocketbuilder.thrust=推力 msg.rocketbuilder.weight=重量 msg.rocketbuilder.fuel=燃料 -msg.rocketbuilder.acc=飞行控制中心 +msg.rocketbuilder.acc=加速度 msg.rocketbuilder.build=建造 msg.rocketbuilder.scan=扫描 -msg.rocketbuild.combinedthrust=燃料类型不能组合! +msg.rocketbuild.combinedthrust=燃料类型不能混合使用! +msg.rocketbuilder.alreadyassembled=火箭已组装完成 +msg.rocketbuilder.nointake=缺少气体收集器! +msg.rocketbuilder.notank=缺少流体储罐! msg.solar.collectingEnergy=收集能量: msg.solar.cannotcollectEnergy=无法收集能量 msg.asteroidChip.asteroid=小行星 -msg.atmanal.atmtype=大气类型: -msg.atmanal.canbreathe=可呼吸: +msg.asteroidChip.type=类型: +msg.atmanal.atmtype=大气类型: +msg.atmanal.canbreathe=是否可呼吸: msg.biomechanger.scan=扫描生物群系 msg.biomechanger.nosat=卫星尚未发射 -msg.biomechanger.selBiome=选中生物群系: -msg.biomechanger.numBiome=扫描的生物群系数量: +msg.biomechanger.selBiome=选择的生物群系: +msg.biomechanger.numBiome=已扫描生物群系数: msg.itemorescanner.nosat=卫星尚未发射 -msg.itemorescanner.maxzoom=最大缩放: -msg.itemorescanner.filter=可过滤矿石: -msg.itemorescanner.value=值: -msg.itemplanetidchip.planetname=星球名: -msg.itemplanetidchip.stationid=站台 Id: -msg.itemplanetidchip.artifacts=工件: -msg.vent.trace=氧迹检测 - -msg.serviceStation.destroyProbNA=销毁概率: N/A -msg.serviceStation.destroyProb=销毁概率 +msg.itemorescanner.maxzoom=最大缩放: +msg.itemorescanner.filter=可过滤矿石: +msg.itemorescanner.value=值: +msg.itemplanetidchip.planetname=星球名称: +msg.itemplanetidchip.stationid=空间站ID: +msg.itemplanetidchip.artifacts=工件: +msg.vent.trace=氧气追踪 + +msg.serviceStation.destroyProbNA=破坏概率:N/A +msg.serviceStation.destroyProb=破坏概率 msg.serviceStation.serviceProgress=服务进度 -msg.serviceStation.serviceProgressNA=服务进度: N/A -msg.serviceStation.wornMotorsText=引擎 +msg.serviceStation.serviceProgressNA=服务进度:N/A +msg.serviceStation.wornMotorsText=发动机 msg.serviceStation.wornSeatsText=座位 -msg.serviceStation.wornTanksText=油箱 -msg.serviceStation.assemblerScan=扫描装配器 -msg.serviceStation.link=你将位于以下位置的服务站对链接器进行编程: - -msg.itemsatellite.pwr=电源存储: -msg.itemsatellite.nopwr=无电源存储 -msg.itemsatellite.pwrgen=发电: -msg.itemsatellite.nopwrgen=无发电! -msg.itemsatellite.microwavestatus=收集电能 -msg.itemsatellite.data=数据存储: -msg.itemsatellite.nodata=无数据存储! -msg.itemsatellite.empty=空机箱 -msg.itemsatellite.weight=机箱重量: +msg.serviceStation.wornTanksText=燃料箱 +msg.serviceStation.assemblerScan=扫描组装机 +msg.serviceStation.link=已将服务站写入链接器,位置: + +msg.itemsatellite.pwr=能量存储: +msg.itemsatellite.nopwr=无能量存储 +msg.itemsatellite.pwrgen=能量产出: +msg.itemsatellite.nopwrgen=无能量产出! +msg.itemsatellite.microwavestatus=收集能量 +msg.itemsatellite.data=数据存储: +msg.itemsatellite.nodata=无数据存储! +msg.itemsatellite.empty=空框架 +msg.itemsatellite.datagen=数据产出:%s/s +msg.itemsatellite.weight=框架重量: msg.itemsatellite.noweight=重量计算错误 +msg.itemsatellite.unassembled=未组装(预览) + + +msg.brokenstage.text=摧毁阶段 -msg.brokenstage.text=销毁阶段 - -msg.itemsatchip.id=ID: -msg.itemsatchip.planet=行星: -msg.itemsatchip.planetunk=行星: 未知 -msg.itemsatchip.sat=卫星: -msg.itemsatchip.satlost=卫星: 失去联系 -msg.sealdetector.sealed=应能很好地密封 -msg.sealdetector.notsealmat=材料不会密封 -msg.sealdetector.notsealblock=阻挡物不会保持密封 -msg.sealdetector.notfullblock=空气会绕开此方块 -msg.sealdetector.fluid=空气会渗透此方块 -msg.sealdetector.other=空气会在此方块处泄露 -msg.stationchip.sation=站台 -msg.entity.rocket.descend.1=按空格键下降! -msg.entity.rocket.descend.2=自动下降 -msg.entity.rocket.ascend.1=按空格键起飞! -msg.entity.rocket.ascend.2=目的地: -msg.entity.rocket.launch=在T区发射 - -msg.entity.rocket.launch2=按空格键终止发射 -msg.entity.rocket.station=发射站 -msg.entity.rocket.pad=平台: +msg.itemsatchip.id=ID: +msg.itemsatchip.planet=星球: +msg.itemsatchip.planetunk=星球: 未知 +msg.itemsatchip.sat=卫星: +msg.itemsatchip.satlost=卫星:失去联系 +msg.sealdetector.sealed=应该能保持良好密封。 +msg.sealdetector.notsealmat=材料无法保持密封。 +msg.sealdetector.notsealblock=方块无法保持密封。 +msg.sealdetector.notfullblock=空气会从这个方块通过。 +msg.sealdetector.fluid=空气会从这个方块中冒泡通过。 +msg.sealdetector.other=空气会从这个方块泄漏。 +msg.stationchip.sation=空间站 +msg.entity.rocket.descend.1=按空格键降落! +msg.entity.rocket.descend.2=自动降落倒计时: +msg.entity.rocket.ascend.1=按空格键发射! +msg.entity.rocket.ascend.2=目的地: +msg.entity.rocket.launch=发射倒计时: +msg.entity.rocket.launch2=按[空格键]中止 +msg.entity.rocket.station=空间站 +msg.entity.rocket.pad=发射台: msg.entity.rocket.disass=解体 msg.entity.rocket.seldst=选择目的地 -msg.entity.rocket.clear=发射 -msg.entity.rocket.rcs=反作用控制系统模式 +msg.entity.rocket.clear=清除 +msg.entity.rocket.rcs=RCS模式 msg.entity.rocket.none=未选择 +msg.entity.rocket.openGuiHint=按%s键打开火箭GUI msg.wirelessTransciever.extract=提取 +msg.wirelessTransciever.insert=插入 +msg.wirelessTransciever.type=类型:%s +msg.wirelessTransciever.network=网络: +msg.wirelessTransciever.network.unlinked=未链接 + +msg.advancedrocketry.planetselector.up=<< 上一级 +msg.advancedrocketry.planetselector.select=选择 +msg.advancedrocketry.planetselector.planet.list=星球列表 +msg.advancedrocketry.planetselector.atm.tooltip=%b -> %a个地球大气压 +msg.advancedrocketry.planetselector.mass.tooltip=%b -> %a个地球质量 +msg.advancedrocketry.planetselector.distance.tooltip=%b -> %a相对距离单位 +msg.advancedrocketry.planetselector.star.tooltip.name=名称:%s +msg.advancedrocketry.planetselector.star.tooltip.number.of.planets=行星数量:%d +msg.advancedrocketry.planetselector.planet.tooltip.name=%s +msg.advancedrocketry.planetselector.planet.tooltip.moons.count=天然卫星:%d + + -msg.powerunit.rfpertick=FE/t -msg.linker.error.firstMachine=这必须是第一台要链接的机器! -msg.linker.program=坐标已编入链接器 -msg.linker.success=链接成功 -msg.notenoughpower=电量不足! +msg.powerunit.rfpertick=RF/t +msg.linker.error.firstMachine=这台机器必须第一个被链接! +msg.linker.program=坐标已写入链接器 +msg.linker.success=已成功链接 +msg.notenoughpower=能量不足! msg.empty=空 msg.yes=是 msg.no=否 msg.connected=已连接 -msg.notconnected=未连接 -msg.unprogrammed=未编程 -msg.programfail=编程失败 -msg.modules=模块 +msg.notconnected=未链接 +msg.unprogrammed=未编写 +msg.programfail=编写失败 +msg.modules=模块: msg.na=N/A -msg.entityDeployedRocket.notGasGiant=这里没有气体 -msg.noOxygen=警告: 氧气浓度不足! -msg.tooHot=警告: 大气过热! -msg.tooDense=警告: 大气压过高! -msg.muchTooDense=警告: 大气压达到临界压力! - -msg.chat.nostation1=你在空间站醒来时,有一种挥之不去的感觉,那就是你那影响深远的太空行走遭到了某位狐神长老的鄙视,如果你再次尝试并期望得到不同的结果,那将是愚蠢的 -msg.chat.nostation2=也许你应该考虑一下,不要再逾越明确的逻辑和绝对的界限,然后决定这是个好主意,出了问题也不是你的错 -msg.chat.nostation3=你必须在空间站上才能来到这个时空,而现在还没有空间站! - -commands.weather.always_not_clear=这个星球的天气总是阴暗... -commands.weather.cannot_rain=这里无法启动降雨 -commands.weather.cannot_thunder=这里无法启动雷暴 +msg.entityDeployedRocket.notGasGiant=没有气体 +msg.noOxygen=警告:大气缺氧! +msg.tooHot=警告:大气过热! +msg.tooDense=警告:大气压力过高! +msg.muchTooDense=警告:大气压力极高! + +msg.chat.nostation1=你在空间站醒来,隐约觉得上次那场深空漫步似乎触怒了某位年长的狐神,若想重蹈覆辙且期待能有不同的结果,实属不智 +msg.chat.nostation2=也许你应该在再次越过明确合理且绝对的界限之前先思考一下,而不是在事情出错后自认为是个好主意且不是你的错 +msg.chat.nostation3=你必须在空间站上才能进入此维度,但目前尚未建造任何空间站! + +# Orbital Registry +msg.orbitalregistry.tab.satellites=卫星: +msg.orbitalregistry.tab.stations=空间站: +msg.orbitalregistry.text.details=详细信息: +msg.orbitalregistry.text.satellites=卫星 +msg.orbitalregistry.text.stations=空间站 +msg.orbitalregistry.text.nosel=选择一个对象 +msg.orbitalregistry.text.notfound=未找到 +msg.orbitalregistry.text.sat.datagen=数据产出: +msg.orbitalregistry.scan.tooltip=更新此列表 +msg.orbitalregistry.writechip.ok=点击编程芯片! +msg.orbitalregistry.writechip.no=无法为此对象编程 +msg.orbitalregistry.writechip=编程芯片 + + +# List entry +msg.orbitalregistry.text.listentry=ID + +# StationDetails +msg.orbitalregistry.text.type.starshiplist=§6星际飞船 +msg.orbitalregistry.text.type.starship=星际飞船 +msg.orbitalregistry.text.type.station=空间站 +msg.orbitalregistry.text.id=ID: +msg.orbitalregistry.text.type=类型: +msg.orbitalregistry.text.dimid=维度: +msg.orbitalregistry.text.dimid.none=无 +msg.orbitalregistry.text.orbit=轨道环绕: +msg.orbitalregistry.text.orbit.unlaunched=未入轨! +msg.orbitalregistry.text.freepads=空闲着陆台: +msg.orbitalregistry.text.anchored=已锚定: +msg.orbitalregistry.text.anchored.yes=是 +msg.orbitalregistry.text.anchored.no=否 +msg.orbitalregistry.text.system=星系: +msg.orbitalregistry.text.system.none=无 +msg.orbitalregistry.text.system.unknown=未知 + +# SatelliteDetails power fields +msg.orbitalregistry.text.sat.pwrgen=能量产出: +msg.orbitalregistry.text.sat.pwrstore=能量存储: +msg.orbitalregistry.text.sat.maxdata=最大数据量: + +# Orbital Registry – satellite type names +msg.orbitalregistry.sat.name.optical=望远镜 +msg.orbitalregistry.sat.name.density=密度 +msg.orbitalregistry.sat.name.composition=成分 +msg.orbitalregistry.sat.name.mass=质量 +msg.orbitalregistry.sat.name.solarEnergy=太阳能 +msg.orbitalregistry.sat.name.oreScanner=矿物扫描仪 +msg.orbitalregistry.sat.name.biomeChanger=生物群系变换器 +msg.orbitalregistry.sat.name.weatherController=天气 + +msg.orbitalregistry.writechip.hint.insert=放入芯片进行写入 +msg.orbitalregistry.writechip.hint.select=从列表中选择一个条目 +msg.orbitalregistry.writechip.hint.output=需先清空输出槽位 +msg.orbitalregistry.writechip.hint.sat.or.stationchip=放入空间站芯片 +msg.orbitalregistry.writechip.hint.sat.or.idchip=放入卫星ID芯片或控制器 +msg.orbitalregistry.writechip.hint.sat.badcontroller=该卫星不接受此芯片,请使用对应的控制器! +msg.orbitalregistry.writechip.hint.sat.orescanner.only=矿石扫描仪仅能链接至矿石勘探卫星 +msg.orbitalregistry.writechip.hint.station.unlaunched=该空间站尚未进入轨道 + + + +commands.weather.always_not_clear=这颗星球从未晴朗过…… +commands.weather.cannot_rain=此处无法降雨 +commands.weather.cannot_thunder=此处无法降下雷暴 + +# Jeistuff +jei.machinerecipe.power=能量: +jei.machinerecipe.time=时间: +jei.sb.satellitepreview=已做好入轨准备! +jei.sb.copy.source=来源 +jei.sb.copy.output=新副本 +jei.sb.assemblyhint=至少需要一块太阳能板 +jei.sb.copychiphint=记得做好备份! +jei.ar.asteroids=小行星 + + +jei.ar.fuel.role.monopropellant=单组元推进剂燃料 +jei.ar.fuel.role.biprop_fuel=双组元推进剂燃料 +jei.ar.fuel.role.oxidizer=氧化剂 +jei.ar.fuel.role.working_fluid=工质 +jei.ar.stationAssembler.newStationChipHint=§c此芯片指向新的空间站! + +# Generic hint +tooltip.advancedrocketry.hold_shift=按住§eShift§7查看详细信息 +tooltip.advancedrocketry.hold_alt=按住§eAlt§7查看高级提示 + +# Fuel Tank (monoprop) +tooltip.advancedrocketry.fueltank=§c火箭的组成部分 +tooltip.advancedrocketry.fueltank.shift.1=§f可容纳:§b%s +tooltip.advancedrocketry.fueltank.alt.1=让我们摇滚冲天! + +# Bipropellant Fuel Tank +tooltip.advancedrocketry.bipropfueltank=§c火箭的组成部分 +tooltip.advancedrocketry.bipropfueltank.shift.1=§f可容纳:§b%s +tooltip.advancedrocketry.bipropfueltank.alt.1=§f双组元推进剂火箭需要§b双组元推进剂§f和§b氧化剂§f燃料箱 + +# Oxidizer Fuel Tank +tooltip.advancedrocketry.oxidizerfueltank=§c火箭的组成部分 +tooltip.advancedrocketry.oxidizerfueltank.shift.1=§f可容纳:§b%s +tooltip.advancedrocketry.oxidizerfueltank.alt.1=§f双组元推进剂火箭需要§b双组元推进剂§f和§b氧化剂§f燃料箱 + +# Nuclear Fuel Tank +tooltip.advancedrocketry.nuclearfueltank=§c火箭的组成部分 +tooltip.advancedrocketry.nuclearfueltank.1=§6可进行行星航行! +tooltip.advancedrocketry.nuclearfueltank.shift.1=§f可容纳:§b%s +tooltip.advancedrocketry.nuclearfueltank.alt.1=§f需要§b核热堆芯§f和§b核热发动机 + +# Monopropellant Engine +tooltip.advancedrocketry.monopropmotor=§c火箭的组成部分 +tooltip.advancedrocketry.monopropmotor.shift.1=§f使用§b单组元推进剂燃料 +tooltip.advancedrocketry.monopropmotor.alt.1=查看加油站的JEI页面 + +# Nuclear Core +tooltip.advancedrocketry.nuclearcore=§c火箭的组成部分 +tooltip.advancedrocketry.nuclearcore.1=§6可进行行星航行! +tooltip.advancedrocketry.nuclearcore.shift.1=必须直接放置在核热发动机 +tooltip.advancedrocketry.nuclearcore.shift.2=或其他核热堆芯正上方。 +tooltip.advancedrocketry.nuclearcore.alt.1=垂直放置规则不适用于 +tooltip.advancedrocketry.nuclearcore.alt.2=无人载具(气体任务火箭) + +# Nuclear rocketengine +tooltip.advancedrocketry.nuclearmotor=§c火箭的组成部分 +tooltip.advancedrocketry.nuclearmotor.1=§6可进行行星航行! +tooltip.advancedrocketry.nuclearmotor.shift.1=§f使用§b工质 +tooltip.advancedrocketry.nuclearmotor.shift.2=正上方需存在核热堆芯。 +tooltip.advancedrocketry.nuclearmotor.alt.1=查看加油站的JEI页面 + +# Bipropellant Engine +tooltip.advancedrocketry.bipropmotor=§c火箭的组成部分 +tooltip.advancedrocketry.bipropmotor.shift.1=§f使用§b双组元推进剂燃料§7和§b氧化剂 +tooltip.advancedrocketry.bipropmotor.alt.1=查看加油站的JEI页面 + +# Drill +tooltip.advancedrocketry.drill=§c火箭的组成部分 +tooltip.advancedrocketry.drill.shift.1=缩短§6采矿任务§f的所需时间 +tooltip.advancedrocketry.drill.shift.2=§b效果可叠加 +tooltip.advancedrocketry.drill.alt.1=此火箭需通过火箭组装机搭建 +tooltip.advancedrocketry.drill.alt.2=需要一个已编程的小行星芯片 + +# Gas Intake +tooltip.advancedrocketry.intake=§c火箭的组成部分 +tooltip.advancedrocketry.intake.shift.1=缩短§6气体任务§f的所需时间 +tooltip.advancedrocketry.intake.shift.2=§b效果可叠加 +tooltip.advancedrocketry.intake.alt.1=此火箭需通过无人载具组装机搭建 +tooltip.advancedrocketry.intake.alt.2=从绕气态巨行星运行的空间站发射 + +# Seat +tooltip.advancedrocketry.seat=§c火箭的组成部分 +tooltip.advancedrocketry.seat.shift.1=为火箭增加一个乘客位 + +# Guidance Computer +tooltip.advancedrocketry.guidancecomputer=§c火箭的组成部分 +tooltip.advancedrocketry.guidancecomputer.shift.1=放入§c芯片§7或已编程的§c链接器§7 +tooltip.advancedrocketry.guidancecomputer.alt.1=部署卫星或空间站至轨道时 +tooltip.advancedrocketry.guidancecomputer.alt.2=记得设定火箭的目标星球 + +# Service Monitor +tooltip.advancedrocketry.servicemonitor=§c火箭的组成部分 +tooltip.advancedrocketry.servicemonitor.shift.1=在火箭界面中 +tooltip.advancedrocketry.servicemonitor.shift.2=启用损伤视图 +tooltip.advancedrocketry.servicemonitor.alt.1=未完成 +tooltip.advancedrocketry.servicemonitor.alt.2= + +# Docking Pad (landingPad) +tooltip.advancedrocketry.landingpad=使用此方块替换发射台结构的中心方块 +tooltip.advancedrocketry.landingpad.shift.1=§c链接器§7能够存储此方块的精确位置(包括维度信息)。 +tooltip.advancedrocketry.landingpad.shift.2=将其放入§4导航计算机§7以在此着陆。 +tooltip.advancedrocketry.landingpad.alt.1=在停靠台中放入一个已编程的链接器。 +tooltip.advancedrocketry.landingpad.alt.2=从此停靠台发射的火箭会飞往链接器中保存的坐标(用于实现自动化)。 +tooltip.advancedrocketry.landingpad.alt.3=如果火箭的导航计算机中没有其他目标信息。 + +# Launch Pad +tooltip.advancedrocketry.launchpad=发射台平台的基础方块。 +tooltip.advancedrocketry.launchpad.shift.1=以方形平台形状放置发射台方块。 +tooltip.advancedrocketry.launchpad.shift.2=3x3、4x4、5x5…… +tooltip.advancedrocketry.launchpad.alt.1=§b添加结构塔(最低高度4格,起始方块需与发射台y坐标相同) +tooltip.advancedrocketry.launchpad.alt.2=§f火箭/空间站组装机将自动连接 + +# Structure Tower +tooltip.advancedrocketry.structuretower=发射台平台的垂直支架。 +tooltip.advancedrocketry.structuretower.shift.1=结构塔最低高度为4格。 +tooltip.advancedrocketry.structuretower.shift.2=底部方块必须与发射台相连。 +tooltip.advancedrocketry.structuretower.alt.1=无人载具组装机的主要方块 +tooltip.advancedrocketry.structuretower.alt.2=查看维基了解更多信息。 + +# Terraformer +tooltip.advancedrocketry.terraformer=改变整颗星球的地形环境! +tooltip.advancedrocketry.terraformer.shift.1=§f需与§c生物群系变换器卫星§f搭配使用 +tooltip.advancedrocketry.terraformer.shift.2=§f卫星必须环绕对应星球运行 +tooltip.advancedrocketry.terraformer.alt.1=§f放入§c生物群系变换器遥控终端 +tooltip.advancedrocketry.terraformer.alt.2=§f由卫星提供能源 + +# Rocket Monitoring Station +tooltip.advancedrocketry.monitoringstation=§c基础设备 +tooltip.advancedrocketry.monitoringstation.shift.1=通过红石信号发射! +tooltip.advancedrocketry.monitoringstation.shift.2=§f需连接至§4火箭组装机/停靠台§f或§4火箭本体 +tooltip.advancedrocketry.monitoringstation.alt.1=§b任务将在进入轨道后激活! + +# Satellite Terminal +tooltip.advancedrocketry.satellitecontrolcenter=用于与卫星通信 +tooltip.advancedrocketry.satellitecontrolcenter.shift.1=放入卫星芯片 +tooltip.advancedrocketry.satellitecontrolcenter.shift.2=下载数据 +tooltip.advancedrocketry.satellitecontrolcenter.alt.1=§f通过无线收发器或数据单元传输并自动下载数据 + +# Satellite Builder +tooltip.advancedrocketry.satellitebuilder=用于组装卫星 +tooltip.advancedrocketry.satellitebuilder.shift.1=也可创建芯片/遥控终端副本。 +tooltip.advancedrocketry.satellitebuilder.shift.2=§b必须放置在能量输入口顶部! +tooltip.advancedrocketry.satellitebuilder.alt.1=放入框架、芯片/遥控终端 +tooltip.advancedrocketry.satellitebuilder.alt.2=核心组件+其他组件 + +# Orbital Registry +tooltip.advancedrocketry.orbitalregistry=追踪太空中的人造物体 +tooltip.advancedrocketry.orbitalregistry.shift.1=§f可写入新芯片! +tooltip.advancedrocketry.orbitalregistry.alt.1=§f在§4卫星终端§f中使用芯片以销毁卫星 + +### Infrastructure +## BlockARHatch + +# TileDataBusBig +tooltip.advancedrocketry.databusbig.header=§c总线与单元 +tooltip.advancedrocketry.databusbig.shift.1=§b只能保存一种类型的数据 +tooltip.advancedrocketry.databusbig.alt.1=§b破坏时保留数据,可作为物品或方块使用 + +# TileDataBus +tooltip.advancedrocketry.hatch.databus=容量:§62000§7 数据 +tooltip.advancedrocketry.hatch.databus.shift.1=§b只能保存一种类型的数据 +tooltip.advancedrocketry.hatch.databus.alt.1=§f破坏时清空数据 + +# TileSatelliteHatch +tooltip.advancedrocketry.hatch.satellite=§c火箭的组成部分 +tooltip.advancedrocketry.hatch.satellite.shift.1=用于将有效载荷送入轨道 +tooltip.advancedrocketry.hatch.satellite.shift.2=记得设定轨道的目标星球! +tooltip.advancedrocketry.hatch.satellite.alt.1=§f可在§4空间站组装机§f中用于封装和存储空间站 + +# TileRocketUnloader +tooltip.advancedrocketry.hatch.item_unloader=§c基础设备 +tooltip.advancedrocketry.hatch.item_unloader.shift.1=空载时发出红石信号 +tooltip.advancedrocketry.hatch.item_unloader.alt.1=§f需连接至§4火箭组装机/停靠台§f或§4火箭本体 +tooltip.advancedrocketry.hatch.item_unloader.alt.2=§f在此降落的火箭将自动建立连接。 + +# TileRocketLoader +tooltip.advancedrocketry.hatch.item_loader=§c基础设备 +tooltip.advancedrocketry.hatch.item_loader.shift.1=满载时发出红石信号 +tooltip.advancedrocketry.hatch.item_loader.alt.1=§f需连接至§4火箭组装机/停靠台§f或§4火箭本体 +tooltip.advancedrocketry.hatch.item_loader.alt.2=§f在此降落的火箭将自动建立连接。 + +# TileRocketFluidUnloader +tooltip.advancedrocketry.hatch.fluid_unloader=§c基础设备 +tooltip.advancedrocketry.hatch.fluid_unloader.shift.1=空载时发出红石信号 +tooltip.advancedrocketry.hatch.fluid_unloader.alt.1=§f需连接至§4火箭组装机/停靠台§f或§4火箭本体 +tooltip.advancedrocketry.hatch.fluid_unloader.alt.2=§f在此降落的火箭将自动建立连接。 + +# TileRocketFluidLoader +tooltip.advancedrocketry.hatch.fluid_loader=§c基础设备 +tooltip.advancedrocketry.hatch.fluid_loader.shift.1=满载时发出红石信号 +tooltip.advancedrocketry.hatch.fluid_loader.alt.1=§f需连接至§4火箭组装机/停靠台§f或§4火箭本体 +tooltip.advancedrocketry.hatch.fluid_loader.alt.2=§f在此降落的火箭将自动建立连接。 + +# Guidance Computer Access +tooltip.advancedrocketry.hatch.gca=§c基础设备 +tooltip.advancedrocketry.hatch.gca.shift.1=空载时发出红石信号 +tooltip.advancedrocketry.hatch.gca.alt.1=§f需连接至§4火箭组装机/停靠台§f或§4火箭本体 +tooltip.advancedrocketry.hatch.gca.alt.2=§f在此降落的火箭将自动建立连接。 + +## /BlockARHatch + +# Fueling Station +tooltip.advancedrocketry.fuelingstation=§c基础设备 +tooltip.advancedrocketry.fuelingstation.shift.1=当空间站燃料类型相同时 +tooltip.advancedrocketry.fuelingstation.shift.2=满载时发出红石信号。 +tooltip.advancedrocketry.fuelingstation.alt.1=§f需连接至§4火箭组装机/停靠台§f或§4火箭本体 +tooltip.advancedrocketry.fuelingstation.alt.2=§f在此降落的火箭将自动建立连接。 + +# Service Station +tooltip.advancedrocketry.servicestation=§c基础设备 +tooltip.advancedrocketry.servicestation.shift.1=修复火箭 +tooltip.advancedrocketry.servicestation.shift.2=§o(未完成) + +## // Infrastructure + +# Pressurized Fluid Tank +tooltip.advancedrocketry.fluidtank.empty=空 +tooltip.advancedrocketry.fluidtank.fluid=流体: +tooltip.advancedrocketry.fluidtank.level=液位: +tooltip.advancedrocketry.fluidtank.shift.1=§c与上下的储罐连接构成大型储罐 + +# Wireless Transciever +tooltip.advancedrocketry.transceiver=传输§6数据 +tooltip.advancedrocketry.transceiver.shift.1=§f使用§c链接器§f创建网络 +tooltip.advancedrocketry.transceiver.shift.2=§f支持连接多个收发器 +tooltip.advancedrocketry.transceiver.alt.1=§f提取模式可切换终端的自动下载功能 +tooltip.advancedrocketry.transceiver.alt.2=§o(如数据过期请重新放入芯片) + +# Atmosphere Detector +tooltip.advancedrocketry.atmosphereDetector=根据大气环境发出红石信号 +tooltip.advancedrocketry.atmosphereDetector.shift.1=选择需要检测的大气类型 +tooltip.advancedrocketry.atmosphereDetector.shift.2=条件符合时即会发射信号 +tooltip.advancedrocketry.atmosphereDetector.alt.1=可检测:空气、真空、 +tooltip.advancedrocketry.atmosphereDetector.alt.2=低氧气、无氧气、高温等多种状态 + +# Station Light +tooltip.advancedrocketry.circlelight=总是发光 +tooltip.advancedrocketry.circlelight.shift.1=不需要 +tooltip.advancedrocketry.circlelight.shift.2=能量或信号 + +# Gas Charge Pad +tooltip.advancedrocketry.oxygencharger=为太空服装填氧气和氢气 +tooltip.advancedrocketry.oxygencharger.shift.1=站在装填台上填充气体 +tooltip.advancedrocketry.oxygencharger.alt.1=不需要能量 + +# Docking Port +tooltip.advancedrocketry.dockingport=标记空间站模块的对接点 +tooltip.advancedrocketry.dockingport.shift.1=在空间站上:设置唯一的“本端ID” +tooltip.advancedrocketry.dockingport.shift.2=在新模块上:设置“目标ID” +tooltip.advancedrocketry.dockingport.alt.1=在空间站组装机中构建新模块 +tooltip.advancedrocketry.dockingport.alt.2=夹具面必须相互对准 + +# Pipe Seal +tooltip.advancedrocketry.pipeseal=具有气密性的孔洞! +tooltip.advancedrocketry.pipeseal.shift.1=用此方块框住1×1孔洞即可实现密封 +tooltip.advancedrocketry.pipeseal.shift.2=§b每个孔洞需要4个方块 +tooltip.advancedrocketry.pipeseal.alt.1=允许管道穿过的同时防止内部氧气泄露 +tooltip.advancedrocketry.pipeseal.alt.2=实体可通过此类开口穿行 + +# Planet Selector (full-screen) +tooltip.advancedrocketry.planetselector=浏览星体 +tooltip.advancedrocketry.planetselector.shift.1=开启全屏星球UI。 +tooltip.advancedrocketry.planetselector.shift.2=浏览星系、行星与天然卫星 +tooltip.advancedrocketry.planetselector.alt.1=可远程设置跃迁控制器的 +tooltip.advancedrocketry.planetselector.alt.2=目标星球 + +# Holographic Planet Selector +tooltip.advancedrocketry.planetholoselector=全息星体显示器 +tooltip.advancedrocketry.planetholoselector.shift.1=在世界中生成全息投影 +tooltip.advancedrocketry.planetholoselector.alt.1=“功能与星球选择器相同 +tooltip.advancedrocketry.planetholoselector.alt.2=但采用3D全息投影显示” + +# Orientation Controller +tooltip.advancedrocketry.orientationctrl=§c空间站控制器 +tooltip.advancedrocketry.orientationctrl.shift.1=自定义角速度 +tooltip.advancedrocketry.orientationctrl.alt.1=§b仅影响视觉效果§7 + +# Gravity Controller +tooltip.advancedrocketry.gravityctrl=§c空间站控制器 +tooltip.advancedrocketry.gravityctrl.shift.1=人造重力! +tooltip.advancedrocketry.gravityctrl.alt.1=红石控制 + +# Altitude Controller +tooltip.advancedrocketry.altitudectrl=§c空间站控制器 +tooltip.advancedrocketry.altitudectrl.shift.1=自定义轨道高度 +tooltip.advancedrocketry.altitudectrl.alt.1=§b仅影响视觉效果§7 +tooltip.advancedrocketry.altitudectrl.alt.2=红石控制 + +# Co2Scrubber +tooltip.advancedrocketry.scrubber=与氧气排放口相邻放置 +tooltip.advancedrocketry.scrubber.shift.1=减少氧气消耗(最多2个) +tooltip.advancedrocketry.scrubber.alt.1=每个净化器可令氧气消耗减半,增加能量消耗。 +tooltip.advancedrocketry.scrubber.alt.2=使用2个净化器时,氧气排放口将不消耗氧气。 + +# Oxygen Vent +tooltip.advancedrocketry.oxygenvent=在密闭房间中产出可呼吸的空气。 +tooltip.advancedrocketry.oxygenvent.shift.1=需要能量和氧气。 +tooltip.advancedrocketry.oxygenvent.shift.2=放置在封闭区域内。 +tooltip.advancedrocketry.oxygenvent.alt.1=可使用二氧化碳净化器代替氧气供应。 +tooltip.advancedrocketry.oxygenvent.alt.2=范围:%s格半径。 + +# Airlock Door +tooltip.advancedrocketry.smallairlock=气密门! +tooltip.advancedrocketry.smallairlock.shift.1=非完全气密,打开时会泄漏 +tooltip.advancedrocketry.smallairlock.alt.1=使用两扇门 +tooltip.advancedrocketry.smallairlock.alt.2=建造一个合适的气闸 + +# Warp Controller +tooltip.advancedrocketry.warpcontroller=将空间站转变为§6星际飞船 +tooltip.advancedrocketry.warpcontroller.shift.1=在空间站上放置§4跃迁控制器§7和§4跃迁核心 +tooltip.advancedrocketry.warpcontroller.shift.2=在行星和恒星系之间进行跃迁旅行 +tooltip.advancedrocketry.warpcontroller.alt.1=界面中会显示位置、目的地和跃迁燃料 +tooltip.advancedrocketry.warpcontroller.alt.2=(你已经做到了!去看看Wiki吧伙计) + +# CarbonScrubberCartridge +tooltip.advancedrocketry.scrubbercart=用于二氧化碳净化器 +tooltip.advancedrocketry.scrubbercart.shift.1=消耗耐久净化空气。 +tooltip.advancedrocketry.atmanalyzer.alt.1=应该能使用超过24小时 + +# Seal Detector +tooltip.advancedrocketry.sealdetector=检测方块是否具有气密性 +tooltip.advancedrocketry.sealdetector.shift.1=§f右击方块使用 + +# Lens +tooltip.advancedrocketry.lens=§c多方块的组成部分 +tooltip.advancedrocketry.lens.shift.1=§b使用全息投影器! + +## SPACE SUIT + COMPONENTS + +# Suit Working Station +tooltip.advancedrocketry.suitworkingstation=安装/移除§5太空服组件 +tooltip.advancedrocketry.suitworkingstation.shift.1=适用于太空服盔甲 +tooltip.advancedrocketry.suitworkingstation.shift.2=(头盔/胸甲/护腿/靴子) +tooltip.advancedrocketry.suitworkingstation.alt.1=不需要能量 + +# Jetpack +tooltip.advancedrocketry.jetpack=§5太空服组件 +tooltip.advancedrocketry.jetpack.shift.1=§d槽位:胸甲§7 + +# AtmosphereAnalyzer +tooltip.advancedrocketry.atmanalyzer=§5太空服组件 +tooltip.advancedrocketry.atmanalyzer.1=§f手持此物品右击来检查大气。 +tooltip.advancedrocketry.atmanalyzer.shift.1=§d槽位:头盔§7 +tooltip.advancedrocketry.atmanalyzer.alt.1=是否可呼吸?取决于大气 +tooltip.advancedrocketry.atmanalyzer.alt.2=显示类型与气压 + +# BeaconFinder +tooltip.advancedrocketry.beaconfinder=§5太空服组件 +tooltip.advancedrocketry.beaconfinder.shift.1=§f显示指向此维度中高级火箭模组信标的HUD箭头 +tooltip.advancedrocketry.beaconfinder.shift.2=§d槽位:头盔§7 +tooltip.advancedrocketry.beaconfinder.alt.1=箭头偏移量相对于你的朝向 +tooltip.advancedrocketry.beaconfinder.alt.2=仅在已注册信标的高级火箭模组维度中工作。 + +# PressureTank +tooltip.advancedrocketry.pressuretank.shift.1=§d槽位:胸甲§7 +tooltip.advancedrocketry.pressuretank.alt.1=§f为太空服储存氧气 +tooltip.advancedrocketry.pressuretank.alt.2=§f为飞行背包储存氢气 + +## Item Upgrade +# 0 = Hover +tooltip.advancedrocketry.itemupgrade.0=§5太空服组件 +tooltip.advancedrocketry.itemupgrade.0.shift.1=§f启用飞行背包的悬停模式 +tooltip.advancedrocketry.itemupgrade.0.shift.2=§d槽位:头盔§7 +tooltip.advancedrocketry.itemupgrade.0.alt.1=需要在胸甲中安装飞行背包。 +tooltip.advancedrocketry.itemupgrade.0.alt.2=§f潜行+开关飞行背包键来激活 + +# 1 = Flight Speed Control Upgrade +tooltip.advancedrocketry.itemupgrade.1=§5太空服组件 +tooltip.advancedrocketry.itemupgrade.1.shift.1=§f提升飞行背包的飞行速度 +tooltip.advancedrocketry.itemupgrade.1.shift.2=§d槽位:护腿§7 +tooltip.advancedrocketry.itemupgrade.1.alt.1=需要在胸甲中安装飞行背包。 +tooltip.advancedrocketry.itemupgrade.1.alt.2=§b效果可堆叠! + +# 2 = Bionic Leg Upgrade (speed) +tooltip.advancedrocketry.itemupgrade.2=§5太空服组件 +tooltip.advancedrocketry.itemupgrade.2.shift.1=§f增加行走速度 +tooltip.advancedrocketry.itemupgrade.2.shift.2=§d槽位:护腿§7 +tooltip.advancedrocketry.itemupgrade.2.alt.1=疾跑以激活 +tooltip.advancedrocketry.itemupgrade.2.alt.2=可与多个模块叠加效果。 + +# 3 = Padded Landing Boots Upgrade (no fall damage; config-aware) +tooltip.advancedrocketry.itemupgrade.3=§5太空服组件 +tooltip.advancedrocketry.itemupgrade.3.shift.1=§f消除摔落伤害 +tooltip.advancedrocketry.itemupgrade.3.shift.2=§d槽位:靴子§7 +tooltip.advancedrocketry.itemupgrade.3.alt.1=无额外叠加效果。 + +# 4 = Antifog Visor Upgrade +tooltip.advancedrocketry.itemupgrade.4=§5太空服组件 +tooltip.advancedrocketry.itemupgrade.4.shift.1=§f看透高气压星球上的迷雾 +tooltip.advancedrocketry.itemupgrade.4.shift.2=§d槽位:头盔§7 +tooltip.advancedrocketry.itemupgrade.4.alt.1=无额外叠加效果。 + +# 5 = Earthbright Visor +tooltip.advancedrocketry.itemupgrade.5=§5太空服组件 +tooltip.advancedrocketry.itemupgrade.5.shift.1=§f调整遥远世界的光照水平 +tooltip.advancedrocketry.itemupgrade.5.shift.2=§d槽位:头盔§7 +tooltip.advancedrocketry.itemupgrade.5.alt.1=无额外叠加效果。 + +## Satellite Components +# Primary Function payloads +tooltip.advancedrocketry.satfunc.optical=§5卫星核心组件 +tooltip.advancedrocketry.satfunc.optical.shift.1=§b收集距离数据§7 +tooltip.advancedrocketry.satfunc.optical.shift.2=§o通过卫星终端下载数据 +tooltip.advancedrocketry.satfunc.optical.alt.1=组装时 +tooltip.advancedrocketry.satfunc.optical.alt.2=与卫星芯片结合 + +tooltip.advancedrocketry.satfunc.composition=§5卫星核心组件 +tooltip.advancedrocketry.satfunc.composition.shift.1=§b收集成分数据§7 +tooltip.advancedrocketry.satfunc.composition.shift.2=§o通过卫星终端下载数据 +tooltip.advancedrocketry.satfunc.composition.alt.1=组装时 +tooltip.advancedrocketry.satfunc.composition.alt.2=与卫星芯片结合 + +tooltip.advancedrocketry.satfunc.mass=§5卫星核心组件 +tooltip.advancedrocketry.satfunc.mass.shift.1=§b收集质量数据§7 +tooltip.advancedrocketry.satfunc.mass.shift.2=§o通过卫星终端下载数据 +tooltip.advancedrocketry.satfunc.mass.alt.1=组装时 +tooltip.advancedrocketry.satfunc.mass.alt.2=与卫星芯片结合 + +tooltip.advancedrocketry.satfunc.microwave=§5卫星核心组件 +tooltip.advancedrocketry.satfunc.microwave.shift.1=§b在太空中产出能量§7 +tooltip.advancedrocketry.satfunc.microwave.shift.2=§o需要微波接收器(5x5多方块结构) +tooltip.advancedrocketry.satfunc.microwave.alt.1=组装时 +tooltip.advancedrocketry.satfunc.microwave.alt.2=与卫星芯片结合 + +tooltip.advancedrocketry.satfunc.oremapping=§5卫星核心组件 +tooltip.advancedrocketry.satfunc.oremapping.shift.1=§b扫描星球上的矿物§7 +tooltip.advancedrocketry.satfunc.oremapping.alt.1=组装时 +tooltip.advancedrocketry.satfunc.oremapping.alt.2=与矿物扫描仪结合 + +tooltip.advancedrocketry.satfunc.biomechanger=§5卫星核心组件 +tooltip.advancedrocketry.satfunc.biomechanger.shift.1=§b调节生物群系§7 +tooltip.advancedrocketry.satfunc.biomechanger.alt.1=组装时与生物群系变换器遥控终端结合 +tooltip.advancedrocketry.satfunc.biomechanger.alt.2=需要能量产出和存储组件! + +tooltip.advancedrocketry.satfunc.weather=§5卫星核心组件 +tooltip.advancedrocketry.satfunc.weather.shift.1=§b处理天气相关事项! +tooltip.advancedrocketry.satfunc.weather.alt.1=组装时与天气遥控终端结合 + +# Weather Remote +tooltip.advancedrocketry.weathercontrollerremote=§bShift-右击打开界面 +tooltip.advancedrocketry.weathercontrollerremote.shift.1=§f手持此物品右击来使用 +tooltip.advancedrocketry.weathercontrollerremote.alt.1=组装时与天气控制器结合 +tooltip.advancedrocketry.weathercontrollerremote.mode.rain=§e模式:降雨——使用水填充地形中的小洼地 +tooltip.advancedrocketry.weathercontrollerremote.mode.dry=§e模式:干旱——蒸发半径16格内的所有水源 +tooltip.advancedrocketry.weathercontrollerremote.mode.flood=§e模式:洪水——用水淹没半径16格的区域 + + +# Biome Changer Remote +tooltip.advancedrocketry.biomechangerremote=§bShift-右击打开界面 +tooltip.advancedrocketry.biomechangerremote.shift.1=§f手持此物品右击来转换20x20的区域 +tooltip.advancedrocketry.biomechangerremote.shift.2=§f“扫描生物群系”会将所在的生物群系储存在卫星的内存中。 +tooltip.advancedrocketry.biomechangerremote.alt.1=§6卫星需要大量能量 +tooltip.advancedrocketry.biomechangerremote.alt.2=§f用于§c环境改造终端§f和§c大气改造器 + +# Ore Scanner +tooltip.advancedrocketry.orescanner=§b右击打开界面 +tooltip.advancedrocketry.orescanner.shift.1=§f若卫星的数据存储容量大于等于§63,000§f,矿物扫描仪可以按类型过滤。 +tooltip.advancedrocketry.orescanner.alt.1=§f扫描范围取决于卫星的能量产出 + +# Power Sources +tooltip.advancedrocketry.satpower.0=§5卫星组件 +tooltip.advancedrocketry.satpower.0.shift.1=§f产能:§c4 §fRF/t§7 +tooltip.advancedrocketry.satpower.0.shift.2=§o卫星需要至少1个产能组件 + +tooltip.advancedrocketry.satpower.1=§5卫星组件 +tooltip.advancedrocketry.satpower.1.shift.1=§f产能:§c40 §fRF/t§7 +tooltip.advancedrocketry.satpower.1.shift.2=§o卫星需要至少1个产能组件 + +# LibVulpes Batteries +tooltip.libvulpes.battery.0=§5卫星组件§7 +tooltip.libvulpes.battery.0.shift.1=增加能量存储 +tooltip.libvulpes.battery.0.shift.2=§f容量:§c10.000 §fRF§7 + +tooltip.libvulpes.battery.1=§5卫星组件 +tooltip.libvulpes.battery.1.shift.1=增加能量存储 +tooltip.libvulpes.battery.1.shift.2=§f容量:§c40.000 §fRF§7 + +# Data Unit +tooltip.advancedrocketry.itemdata.header=§5卫星组件 +tooltip.advancedrocketry.itemdata.type=§f类型: +tooltip.advancedrocketry.itemdata.data=§f数据存储量: +tooltip.advancedrocketry.itemdataunit.shift.1=§f令卫星数据存储量增加1000 +tooltip.advancedrocketry.itemdataunit.alt.1=§f也可在物品栏中作为数据存储器使用 + +## Chips / remotes +# Asteroid Chip +tooltip.advancedrocketry.asteroidchip.shift.1=§b用于采矿任务§7 +tooltip.advancedrocketry.asteroidchip.shift.2=§f在§c瞭望台§f中编程 +tooltip.advancedrocketry.asteroidchip.alt.1=§4将已编程的芯片放入导航计算机§7 +tooltip.advancedrocketry.asteroidchip.alt.2=§f任务完成后会返还芯片 + +# Station Chip +tooltip.advancedrocketry.stationchip=§b记得做好备份! +tooltip.advancedrocketry.stationchip.shift.1=§f在空间站组装机中编程 +tooltip.advancedrocketry.stationchip.shift.2=§c在卫星组装机中复制 +tooltip.advancedrocketry.stationchip.alt.1=§4将已编程的芯片放入导航计算机§7 +tooltip.advancedrocketry.stationchip.namelabel=名称: + +# Planet Chip +tooltip.advancedrocketry.planetidchip=§b可重复编程! +tooltip.advancedrocketry.planetidchip.shift.1=§f将芯片放入§4导航计算机§7 +tooltip.advancedrocketry.planetidchip.shift.2=§f在火箭界面中设置目的地进行编程。 +tooltip.advancedrocketry.planetidchip.alt.1=§4发射前请仔细检查是否已完成编程! + +# Satellite Chip +tooltip.advancedrocketry.satidchip=§b记得做好备份! +tooltip.advancedrocketry.satidchip.shift.1=§f储存卫星的ID。 +tooltip.advancedrocketry.satidchip.shift.2=§c在卫星组装机中复制 +tooltip.advancedrocketry.satidchip.alt.1=§4在卫星终端中使用以进行链接,或在微波接收器多方块结构(输入仓)中使用。 +tooltip.advancedrocketry.satidchip.alt.2=§8(星球:放入终端时解析信息) + +# Elevator Chip +tooltip.advancedrocketry.elevatorchip=太空电梯芯片 +tooltip.advancedrocketry.elevatorchip.shift.1=§f链接电梯平台/目的地。 + +## Multiblocks +# Black Hole Generator +tooltip.advancedrocketry.blackholegen=通过压缩质量产出能量 +tooltip.advancedrocketry.blackholegen.shift.1=§b使用全息投影器! + +# Microwave Receiver +tooltip.advancedrocketry.microwavereceiver=接收来自太阳能卫星的能量 +tooltip.advancedrocketry.microwavereceiver.shift.1=5x5多方块结构 +tooltip.advancedrocketry.microwavereceiver.shift.2=§b使用全息投影器! +tooltip.advancedrocketry.microwavereceiver.alt.1=§f使用§b微波传输器§f和§b卫星芯片§f来建造太阳能卫星 +tooltip.advancedrocketry.microwavereceiver.alt.2=§8产能 = 卫星产能总和 * (2 * 大气密度系数) + +# Solar Panel (part of Microwave Receiver) +tooltip.advancedrocketry.solarpanel=§c多方块结构的组成部分 +tooltip.advancedrocketry.solarpanel.shift.1=5x5多方块结构 +tooltip.advancedrocketry.solarpanel.shift.2=§o§f(微波接收器) +tooltip.advancedrocketry.solarpanel.shift.3=§b使用全息投影器! + +# Solar Array Controller +tooltip.advancedrocketry.solararray=利用阳光产出能量 +tooltip.advancedrocketry.solararray.shift.1=需要63个阵列太阳能板 +tooltip.advancedrocketry.solararray.shift.2=§b使用全息投影器! + +# Solar Array Panel +tooltip.advancedrocketry.solararraypanel=§c多方块结构的组成部分 +tooltip.advancedrocketry.solararraypanel.shift.1=需要63个阵列太阳能板和控制器 +tooltip.advancedrocketry.solararraypanel.shift.2=§b使用全息投影器! + +# Solar Generator +tooltip.advancedrocketry.solargenerator=基础太阳能板 +tooltip.advancedrocketry.solargenerator.shift.1=§f产能:§c2 §fRF/t§7 + +# Arc Furnace +tooltip.advancedrocketry.arcfurnace=通过极端温度熔炼物质 +tooltip.advancedrocketry.arcfurnace.shift.1=§b使用全息投影器! + +# Rolling Machine +tooltip.advancedrocketry.rollingmachine=加工板材与箔材 +tooltip.advancedrocketry.rollingmachine.shift.1=§b使用全息投影器! + +# Lathe +tooltip.advancedrocketry.lathe=车削加工杆件与轴类零件 +tooltip.advancedrocketry.lathe.shift.1=§b使用全息投影器! + +# Crystallizer +tooltip.advancedrocketry.crystallizer=生长高纯度晶体 +tooltip.advancedrocketry.crystallizer.shift.1=§b使用全息投影器! + +# Cutting Machine +tooltip.advancedrocketry.cuttingmachine=对材料进行精密切割 +tooltip.advancedrocketry.cuttingmachine.shift.1=§b使用全息投影器! + +# Precision Assembler +tooltip.advancedrocketry.precisionassembler=自动化完成复杂组装 +tooltip.advancedrocketry.precisionassembler.shift.1=§b使用全息投影器! + +# Electrolyser +tooltip.advancedrocketry.electrolyser=通过电解工艺分解化合物 +tooltip.advancedrocketry.electrolyser.shift.1=§b使用全息投影器! + +# Chemical Reactor +tooltip.advancedrocketry.chemreactor=处理化学反应 +tooltip.advancedrocketry.chemreactor.shift.1=§b使用全息投影器! + +# Precision Laser Etcher +tooltip.advancedrocketry.precisionlaseretcher=通过激光刻蚀精密电路 +tooltip.advancedrocketry.precisionlaseretcher.shift.1=§b使用全息投影器! + +# Observatory +tooltip.advancedrocketry.observatory=分析天体 +tooltip.advancedrocketry.observatory.shift.1=§f用于§6采矿任务 +tooltip.advancedrocketry.observatory.shift.2=§b使用全息投影器! +tooltip.advancedrocketry.observatory.alt.1=§f放入§c小行星芯片 +tooltip.advancedrocketry.observatory.alt.2=§f需要§6距离数据§f才能运作(仅在夜间工作) + +# LibVulpes Hatches and Coal Generator +tooltip.advancedrocketry.libvulpes.hatch=§c多方块结构的组成部分 +tooltip.advancedrocketry.libvulpes.hatch.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.libvulpes.forgepoweroutput=§c多方块结构的组成部分 +tooltip.advancedrocketry.libvulpes.forgepoweroutput.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.libvulpes.forgepowerinput=§c多方块结构的组成部分 +tooltip.advancedrocketry.libvulpes.forgepowerinput.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.libvulpes.creativepowerbattery=§d无限能源 +tooltip.advancedrocketry.libvulpes.creativepowerbattery.shift.1=§c多方块结构的组成部分 +tooltip.advancedrocketry.libvulpes.creativepowerbattery.shift.2=§b使用全息投影器! +tooltip.advancedrocketry.libvulpes.coalgenerator=§c燃烧固体燃料 + + +# Planet Analyser +tooltip.advancedrocketry.planetanalyser=处理数据并写入小行星芯片 +tooltip.advancedrocketry.planetanalyser.shift.1=§b使用全息投影器! + +# Centrifuge +tooltip.advancedrocketry.centrifuge=按密度分离物质 +tooltip.advancedrocketry.centrifuge.shift.1=§b使用全息投影器! + +# Warp Core +tooltip.advancedrocketry.warpcore=§6星际飞船§f的核心 +tooltip.advancedrocketry.warpcore.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.warpcore.alt.1=§6星际飞船§7需要§4跃迁控制器§7+§4跃迁核心§7 + +# Beacon +tooltip.advancedrocketry.beacon=远程信标 +tooltip.advancedrocketry.beacon.shift.1=§b使用全息投影器! + +# Biome Scanner +tooltip.advancedrocketry.biomescan=扫描星球的生物群系 +tooltip.advancedrocketry.biomescan.shift.1=§b使用全息投影器! + +# Railgun +tooltip.advancedrocketry.railgun=将物品发射到太空 +tooltip.advancedrocketry.railgun.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.railgun.alt.1=“轨道炮的威力不足以在行星间运输物品,其射程仅限于同一行星系内的天体” + +# Space Elevator Controller +tooltip.advancedrocketry.spaceelevatorctrl=用于控制太空电梯 +tooltip.advancedrocketry.spaceelevatorctrl.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.spaceelevatorctrl.shift.2=§f锚定太空站 +tooltip.advancedrocketry.spaceelevatorctrl.alt.1=§f星球端需要上方有空气,空间站端需要下方有空气 +tooltip.advancedrocketry.spaceelevatorctrl.alt.2=§c使用链接器 +# Atmosphere Terraformer +tooltip.advancedrocketry.atmosterraformer=改变整颗星球的大气压 +tooltip.advancedrocketry.atmosterraformer.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.atmosterraformer.alt.1=§f通过连接的§c生物群系变换器遥控终端§f增加和减少大气压。 + +# Area Gravity Controller +tooltip.advancedrocketry.gravitymachine=操纵重力 +tooltip.advancedrocketry.gravitymachine.shift.1=§f也可影响重力的方向 +tooltip.advancedrocketry.gravitymachine.shift.2=§b使用全息投影器! + +# Orbital Last Drill +tooltip.advancedrocketry.spacelaser=§c空间站多方块 +tooltip.advancedrocketry.spacelaser.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.spacelaser.alt.1=§f需要红石信号来运行 + +# Force Field Projector +tooltip.advancedrocketry.forcefieldprojector=最远可投射32格距离! +tooltip.advancedrocketry.forcefieldprojector.shift.1=§f使用红石信号激活 + +# Vacuum Laser +tooltip.advancedrocketry.vacuumlaser=§c多方块结构的组成部分 +tooltip.advancedrocketry.vacuumlaser.shift.1=§b使用全息投影器! + + +# Pump +tooltip.advancedrocketry.pump=搜索下方的流体 +tooltip.advancedrocketry.pump.shift.1=§c可从64格范围内的相连流体池中抽取。 +tooltip.advancedrocketry.pump.alt.1=§f自动弹出到附近储罐 +tooltip.advancedrocketry.pump.alt.2=使用红石信号关闭 + +# Parts for Multiblock +tooltip.advancedrocketry.concrete=§c多方块结构的组成部分 +tooltip.advancedrocketry.concrete.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.blastbrick=§c多方块结构的组成部分 +tooltip.advancedrocketry.blastbrick.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.qcrucible=§c多方块结构的组成部分 +tooltip.advancedrocketry.qcrucible.shift.1=§b使用全息投影器! +tooltip.advancedrocketry.sawblade=§c多方块结构的组成部分 +tooltip.advancedrocketry.sawblade.shift.1=§b使用全息投影器! +tooltip.libvulpes.structuremachine=§c多方块结构的组成部分 +tooltip.libvulpes.structuremachine.shift.1=§b使用全息投影器! +tooltip.libvulpes.advstructuremachine=§c多方块结构的组成部分 +tooltip.libvulpes.advstructuremachine.shift.1=§b使用全息投影器! + + +## Assemblers +# Rocket Assembler +tooltip.advancedrocketry.rocketassembler=§c搭建火箭 +tooltip.advancedrocketry.rocketassembler.shift.1=§b需要发射台+结构塔结构 +tooltip.advancedrocketry.rocketassembler.shift.2=§f将所有基础设备连接至此方块,使它们自动与发射台上的火箭连接 +tooltip.advancedrocketry.rocketassembler.alt.1=§f将此方块放置在比发射台高1格的位置,连接底部边角且朝向与发射台方块相反 + +# Station Assembler +tooltip.advancedrocketry.stationassembler=§c将空间站打包以便发射! +tooltip.advancedrocketry.stationassembler.shift.1=§b需要发射台+结构塔结构 +tooltip.advancedrocketry.stationassembler.alt.1=§f将此方块放置在比发射台高1格的位置,连接底部边角且朝向与发射台方块相反 + +# Packet Station +tooltip.advancedrocketry.packedstructure=放入§c卫星仓§7以送入轨道! +tooltip.advancedrocketry.packedstructure.shift.1=§e发射前记得要设置火箭的目标星球! + +# Deployable Rocket Assembler +tooltip.advancedrocketry.deployablerocketassembler=§c在空间站中搭建火箭 +tooltip.advancedrocketry.deployablerocketassembler.shift.1=§b需要结构塔方块 +tooltip.advancedrocketry.deployablerocketassembler.shift.2=§f用于§6气体任务§7 +tooltip.advancedrocketry.deployablerocketassembler.alt.1=§f将此方块放置在倒T形结构的中间,朝向太空外部,然后从塔顶向外延伸出水平支撑。 +tooltip.advancedrocketry.deployablerocketassembler.alt.2=如果不确定,请查看Wiki! + +# Hovercraft +tooltip.advancedrocketry.hovercraft=使用持久耐用的双锂动力源。可能比你的寿命还长。 +tooltip.advancedrocketry.hovercraft.alt.1=§f检查按键绑定中的转向按键 + +# Thermite +tooltip.advancedrocketry.thermite=以产生高温而闻名! + +# Thermite Torch +tooltip.advancedrocketry.thermitetorch=即便没有氧气也可燃烧 +tooltip.advancedrocketry.thermitetorch.shift.1=§f适用于大气稀薄或真空环境 + +# Jackhammer +tooltip.advancedrocketry.jackhammer=§c极速挖掘工具 +tooltip.advancedrocketry.jackhammer.1=这东西估计能比你活得还久(当然,得记得换螺栓) +tooltip.advancedrocketry.jackhammer.shift.1=§f使用§6钛棒§f修复 +tooltip.advancedrocketry.jackhammer.alt.1=§f挖掘等级:§b钻石 + +# Basic basicLaserGun +tooltip.advancedrocketry.lasergun=§c远程挖掘工具 +tooltip.advancedrocketry.lasergun.shift.1=§d无限耐久 +tooltip.advancedrocketry.lasergun.alt.1=§f挖掘等级:§b钻石 +tooltip.advancedrocketry.lasergun.alt.2=§f范围:§b50格 + +## Crafting items +tooltip.advancedrocketry.sawbladeiron=§3合成用物品 +tooltip.advancedrocketry.wafer=§3合成用物品 +tooltip.advancedrocketry.circuitplate=§3合成用物品 +tooltip.advancedrocketry.circuitic=§3合成用物品 +tooltip.advancedrocketry.miscpart=§3合成用物品 +tooltip.advancedrocketry.itemlens=§3合成用物品 +tooltip.advancedrocketry.misc=§3合成用物品 diff --git a/src/main/resources/assets/advancedrocketry/models/block/databusbig.json b/src/main/resources/assets/advancedrocketry/models/block/databusbig.json new file mode 100644 index 000000000..cf285526e --- /dev/null +++ b/src/main/resources/assets/advancedrocketry/models/block/databusbig.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "advancedrocketry:blocks/dataHatchbig" + } +} diff --git a/src/main/resources/assets/advancedrocketry/models/block/guidancecomputeraccesshatch.json b/src/main/resources/assets/advancedrocketry/models/block/guidancecomputeraccesshatch.json index 64a7115ed..7c3692ed9 100644 --- a/src/main/resources/assets/advancedrocketry/models/block/guidancecomputeraccesshatch.json +++ b/src/main/resources/assets/advancedrocketry/models/block/guidancecomputeraccesshatch.json @@ -1,8 +1,6 @@ { - "parent": "block/orientable", + "parent": "block/cube_all", "textures": { - "top": "advancedrocketry:blocks/guidancecomputeraccesshatch", - "front": "advancedrocketry:blocks/guidancecomputeraccesshatch", - "side": "advancedrocketry:blocks/guidancecomputeraccesshatch" + "all": "advancedrocketry:blocks/guidancecomputeraccesshatch" } } diff --git a/src/main/resources/assets/advancedrocketry/models/block/orbitalregistry.json b/src/main/resources/assets/advancedrocketry/models/block/orbitalregistry.json new file mode 100644 index 000000000..855beb1cd --- /dev/null +++ b/src/main/resources/assets/advancedrocketry/models/block/orbitalregistry.json @@ -0,0 +1,8 @@ +{ + "parent": "minecraft:block/orientable", + "textures": { + "top": "libvulpes:blocks/machinegeneric", + "front": "advancedrocketry:blocks/orbitalregistry", + "side": "libvulpes:blocks/machinegeneric" + } +} diff --git a/src/main/resources/assets/advancedrocketry/models/item/databusbig.json b/src/main/resources/assets/advancedrocketry/models/item/databusbig.json new file mode 100644 index 000000000..28454a208 --- /dev/null +++ b/src/main/resources/assets/advancedrocketry/models/item/databusbig.json @@ -0,0 +1,3 @@ +{ + "parent": "advancedrocketry:block/databusbig" +} diff --git a/src/main/resources/assets/advancedrocketry/models/item/orbitalregistry.json b/src/main/resources/assets/advancedrocketry/models/item/orbitalregistry.json new file mode 100644 index 000000000..33b944c0c --- /dev/null +++ b/src/main/resources/assets/advancedrocketry/models/item/orbitalregistry.json @@ -0,0 +1,3 @@ +{ + "parent": "advancedrocketry:block/orbitalRegistry" +} diff --git a/src/main/resources/assets/advancedrocketry/recipes/databusbig.json b/src/main/resources/assets/advancedrocketry/recipes/databusbig.json new file mode 100644 index 000000000..827a2f8f8 --- /dev/null +++ b/src/main/resources/assets/advancedrocketry/recipes/databusbig.json @@ -0,0 +1,37 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": + [ + " a ", + "cmb", + " a " + ], + "key": + { + "c": + { + "item": "advancedrocketry:loader", + "data": 0 + }, + "m": + { + "item": "libvulpes:advstructuremachine" + }, + "a": + { + "item": "advancedrocketry:ic", + "data": 2 + }, + "b": + { + "item": "advancedrocketry:dataunit", + "data": 0 + } + }, + "result": + { + "item": "advancedrocketry:databusbig", + "data": 0, + "count": 1 + } +} diff --git a/src/main/resources/assets/advancedrocketry/recipes/orbitalregistry.json b/src/main/resources/assets/advancedrocketry/recipes/orbitalregistry.json new file mode 100644 index 000000000..d628c62b7 --- /dev/null +++ b/src/main/resources/assets/advancedrocketry/recipes/orbitalregistry.json @@ -0,0 +1,45 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": + [ + "oso", + "cbc", + "rpr" + ], + "key": + { + "s": + { + "item": "advancedrocketry:misc", + "data": 0 + }, + "o": + { + "item": "advancedrocketry:satelliteprimaryfunction", + "data": 0 + }, + "b": + { + "item": "libvulpes:structuremachine" + }, + "c": + { + "type": "forge:ore_dict", + "ore": "stickCopper" + }, + "p": + { + "type": "forge:ore_dict", + "ore": "itemBattery" + }, + "r": + { + "item": "minecraft:comparator" + } + }, + "result": + { + "item": "advancedrocketry:orbitalregistry", + "count": 1 + } +} diff --git a/src/main/resources/assets/advancedrocketry/textures/blocks/datahatchbig.png b/src/main/resources/assets/advancedrocketry/textures/blocks/datahatchbig.png new file mode 100644 index 000000000..add83418e Binary files /dev/null and b/src/main/resources/assets/advancedrocketry/textures/blocks/datahatchbig.png differ diff --git a/src/main/resources/assets/advancedrocketry/textures/blocks/orbitalregistry.png b/src/main/resources/assets/advancedrocketry/textures/blocks/orbitalregistry.png new file mode 100644 index 000000000..66e68a7cb Binary files /dev/null and b/src/main/resources/assets/advancedrocketry/textures/blocks/orbitalregistry.png differ diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index a879bb7ae..bd24c88fe 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -1,18 +1,14 @@ [ -{ - "modid": "advancedrocketry", - "name": "Advanced Rocketry", - "description": "A space mod for minecraft, adds planets, rockets, and some machines", - "version": "${advRocketryVersion}", - "mcversion": "${mcVersion}", - "url": "https://www.curseforge.com/minecraft/mc-mods/advanced-rocketry-2", - "updateUrl": "", - "authorList": ["zmaster587"], - "credits": "", - "logoFile": "", - "screenshots": [], - "useDependencyInformation": true, - "requiredMods": ["libvulpes@${libVulpesVersion}"], - "dependencies": ["libvulpes"] -} -] + { + "modid": "${mod_id}", + "name": "${mod_name}", + "version": "${mod_version}", + "mcversion": "1.12.2", + "description": "${mod_description}", + "authorList": ["${mod_authors}"], + "credits": "${mod_credits}", + "url": "${mod_url}", + "updateJSON": "${mod_update_json}", + "logoFile": "${mod_logo_path}" + } +] \ No newline at end of file diff --git a/tags.properties b/tags.properties new file mode 100644 index 000000000..d795ef9dd --- /dev/null +++ b/tags.properties @@ -0,0 +1,3 @@ +VERSION = ${mod_version} +MOD_ID = ${mod_id} +MOD_NAME = ${mod_name} \ No newline at end of file