diff --git a/changelog.md b/changelog.md new file mode 100644 index 000000000..4e7227f7d --- /dev/null +++ b/changelog.md @@ -0,0 +1,258 @@ +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/gradle.properties b/gradle.properties index f80f2cd54..163fe0461 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.daemon=false # Project mcVersion=1.12.2 forgeVersion=14.23.5.2860 -modVersion=2.1.10 +modVersion=2.2.2 archiveBase=AdvancedRocketry startGitRev=8e676bd diff --git a/src/main/java/zmaster587/advancedRocketry/AdvancedRocketry.java b/src/main/java/zmaster587/advancedRocketry/AdvancedRocketry.java index 77d37f955..93e38a8a7 100644 --- a/src/main/java/zmaster587/advancedRocketry/AdvancedRocketry.java +++ b/src/main/java/zmaster587/advancedRocketry/AdvancedRocketry.java @@ -58,6 +58,7 @@ 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; @@ -95,6 +96,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.*; @@ -374,6 +376,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 +430,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 +632,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 +653,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 +672,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 +705,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 +836,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 +854,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 +883,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 +1058,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); diff --git a/src/main/java/zmaster587/advancedRocketry/api/ARConfiguration.java b/src/main/java/zmaster587/advancedRocketry/api/ARConfiguration.java index 617d27f1f..cd56c0681 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; @@ -366,6 +376,7 @@ public static void loadPreInit() { 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.dataBusBigMultiplier = config.getInt("dataBusBigMultiplier", Configuration.CATEGORY_GENERAL, 4, 1, 20, "Multiplier applied to the Data Bus (Big) max data 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"); @@ -374,7 +385,7 @@ public static void loadPreInit() { 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(); + arConfig.laserDrillPlanet = config.get(Configuration.CATEGORY_GENERAL, "laserDrillPlanet", false, "If true the orbital laser will actually mine blocks on the planet below, (false makes it a VoidMiner with improved performance especially when using Void Cobble: ON in the GUI)").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(); @@ -384,6 +395,9 @@ public static void loadPreInit() { 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.enableOrbitalRegistry = config.get(Configuration.CATEGORY_GENERAL,"EnableOrbitalRegistry",true, "Enables the orbital registry machine").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(); @@ -412,6 +426,16 @@ public static void loadPreInit() { 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.gasHarvestAmountMultiplier = config.get( + MISSION, "gasHarvestAmountMultiplier", 1.0, + "Per-mission harvest cap = 64,000 mB × multiplier. Actual yield = min(cap, total free tank space at launch). Ignored if gasHarvestInfinite=true." + ).getDouble(); + + arConfig.gasHarvestInfinite = config.get( + MISSION, "gasHarvestInfinite", false, + "Fill all attached tanks up to their total free space at launch, capped at 2,147,483,647 mB per mission." + ).getBoolean(); + //Energy Production arConfig.solarGeneratorMult = config.get(ENERGY, "solarGeneratorMultiplier", 1, "Amount of power per tick the solar generator should produce").getInt(); @@ -449,6 +473,7 @@ public static void loadPreInit() { //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(); + 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 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(); @@ -507,7 +532,6 @@ public static void loadPreInit() { 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"); - //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/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/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..b4549e571 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()); @@ -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..342609804 --- /dev/null +++ b/src/main/java/zmaster587/advancedRocketry/client/TooltipInjector.java @@ -0,0 +1,389 @@ +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"); + + // ---- 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/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/WorldCommand.java b/src/main/java/zmaster587/advancedRocketry/command/WorldCommand.java index 3ae264070..fc7fbe9c2 100644 --- a/src/main/java/zmaster587/advancedRocketry/command/WorldCommand.java +++ b/src/main/java/zmaster587/advancedRocketry/command/WorldCommand.java @@ -28,6 +28,7 @@ import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.advancedRocketry.dimension.DimensionProperties; import zmaster587.advancedRocketry.integration.CompatibilityMgr; +import zmaster587.advancedRocketry.item.IDataItem; import zmaster587.advancedRocketry.item.ItemData; import zmaster587.advancedRocketry.item.ItemMultiData; import zmaster587.advancedRocketry.item.ItemStationChip; @@ -176,15 +177,16 @@ private void commandFillData(ICommandSender sender, String[] cmdstring) { return; } - if (!stack.isEmpty() && stack.getItem() instanceof ItemData) { - ItemData item = (ItemData) stack.getItem(); - int dataAmount = item.getMaxData(stack.getItemDamage()); + if (!stack.isEmpty() && stack.getItem() instanceof IDataItem) { + IDataItem item = (IDataItem) 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("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()) { @@ -192,21 +194,23 @@ private void commandFillData(ICommandSender sender, String[] cmdstring) { value.append(data.name().toLowerCase()).append(", "); } } - sender.sendMessage(new TextComponentString("Try " + value)); return; } - if (cmdstring.length >= 3) + + 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("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); 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/DimensionProperties.java b/src/main/java/zmaster587/advancedRocketry/dimension/DimensionProperties.java index 4a3f7bfe6..645f70ad0 100644 --- a/src/main/java/zmaster587/advancedRocketry/dimension/DimensionProperties.java +++ b/src/main/java/zmaster587/advancedRocketry/dimension/DimensionProperties.java @@ -1082,7 +1082,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 +1629,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)) + rainStartLength = nbt.getInteger("rainStartLength"); + if (nbt.hasKey("thunderStartLength", NBT.TAG_INT)) + thunderStartLength = nbt.getInteger("thunderStartLength"); + if (nbt.hasKey("rainProlongationLength", NBT.TAG_INT)) + rainProlongationLength = nbt.getInteger("rainProlongationLength"); + if (nbt.hasKey("thunderProlongationLength", NBT.TAG_INT)) + thunderProlongationLength = nbt.getInteger("thunderProlongationLength"); + + if (nbt.hasKey("rainMarker", NBT.TAG_INT)) + rainMarker = nbt.getInteger("rainMarker"); + if (nbt.hasKey("thunderMarker", NBT.TAG_INT)) + thunderMarker = nbt.getInteger("thunderMarker"); + + // Sanity clamp + if (rainStartLength <= 0) rainStartLength = 168000; + if (thunderStartLength <= 0) thunderStartLength = 168000; + if (rainProlongationLength <= 0) rainProlongationLength = 12000; + if (thunderProlongationLength <= 0) thunderProlongationLength = 12000; + // Clamp markers to documented range + rainMarker = MathHelper.clamp(rainMarker, -1, 1); + thunderMarker = MathHelper.clamp(thunderMarker, -1, 1); //Hierarchy diff --git a/src/main/java/zmaster587/advancedRocketry/entity/EntityRocket.java b/src/main/java/zmaster587/advancedRocketry/entity/EntityRocket.java index 96a725916..952544266 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,16 @@ 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.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.*; @@ -90,6 +95,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 +131,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 +147,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 +201,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 +306,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 +503,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 +886,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 +945,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 +1228,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 +1352,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 +1561,7 @@ else if (distanceSq > this.spacePosition.world.getRenderSizePlanetView() * this. this.motionY = 0; this.setInFlight(false); this.setInOrbit(false); + releaseDestinationPreload(); } @@ -1799,7 +2073,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 +2088,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 +2102,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 +2239,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 +2254,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 +2279,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); } @@ -1980,6 +2328,7 @@ public void copyDataFromOld(Entity entityIn) { nbttagcompound.removeTag("Passengers"); this.readFromNBT(nbttagcompound); this.timeUntilPortal = entityIn.timeUntilPortal; + clearPlanetSelectorCache(); } protected void readNetworkableNBT(NBTTagCompound nbt) { @@ -2148,9 +2497,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 +2609,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 +2650,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 +2741,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 +2800,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 +2826,7 @@ public List getModules(int ID, EntityPlayer player) { @Override public String getModularInventoryName() { - return "Rocket"; + return ""; } @Override @@ -2460,14 +2838,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 +2870,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 +2933,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 +2955,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/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/integration/jei/ARPlugin.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/ARPlugin.java index 22a596f1d..addaa0c5d 100644 --- a/src/main/java/zmaster587/advancedRocketry/integration/jei/ARPlugin.java +++ b/src/main/java/zmaster587/advancedRocketry/integration/jei/ARPlugin.java @@ -17,12 +17,18 @@ 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; @@ -41,7 +47,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 +76,10 @@ 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 IJeiHelpers jeiHelpers; //AR machines can reload recipes. We still need this for JEI to be up-to-date @@ -74,17 +93,23 @@ 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) + ); } @Override @@ -128,7 +153,12 @@ public Object getIngredientUnderMouse(GuiModular guiContainer, new ArcFurnaceRecipeHandler(), new PlatePressRecipeHandler(), new CentrifugeRecipeHandler(), - new PrecisionLaserEtcherRecipeHandler()); + new PrecisionLaserEtcherRecipeHandler(), + new SatelliteBuilderRecipeHandler(), + new FuelingStationRecipeHandler(), + new Co2ScrubberRecipeHandler(), + new StationAssemblerRecipeHandler() + ); registry.addRecipes(RollingMachineRecipeMaker.getMachineRecipes(jeiHelpers, TileRollingMachine.class), rollingMachineUUID); registry.addRecipes(LatheRecipeMaker.getMachineRecipes(jeiHelpers, TileLathe.class), latheUUID); @@ -141,7 +171,10 @@ 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.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockRollingMachine), rollingMachineUUID); registry.addRecipeCatalyst(new ItemStack(AdvancedRocketryBlocks.blockLathe), latheUUID); @@ -154,5 +187,20 @@ 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 + } } diff --git a/src/main/java/zmaster587/advancedRocketry/integration/jei/MachineRecipe.java b/src/main/java/zmaster587/advancedRocketry/integration/jei/MachineRecipe.java index 927b939f8..0116217d7 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,18 @@ 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); + String timeString = String.format("%s %d s", timeLabel, time / 20); fontRendererObj.drawString(timeString, recipeWidth - 55, 55, Color.black.getRGB()); - } } 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/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/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/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..cb7bea633 100755 --- a/src/main/java/zmaster587/advancedRocketry/item/ItemHovercraft.java +++ b/src/main/java/zmaster587/advancedRocketry/item/ItemHovercraft.java @@ -101,6 +101,6 @@ 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")); + tooltip.add(LibVulpes.proxy.getLocalizedString("tooltip.advancedrocketry.hovercraft")); } } 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..2f777071d 100644 --- a/src/main/java/zmaster587/advancedRocketry/mission/MissionOreMining.java +++ b/src/main/java/zmaster587/advancedRocketry/mission/MissionOreMining.java @@ -34,8 +34,38 @@ 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 + } } + + 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() { 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/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..69a3c44ba 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,20 @@ 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 + + + + // 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 +66,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 +154,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 +207,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 +273,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 +308,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; + + // Recompute throttle phase after we cleared it + if (transferIntervalTicks <= 0) transferIntervalTicks = 20; + phase = (int) Math.floorMod(this.pos.toLong(), transferIntervalTicks); - if (extractMode) + // 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..613537e31 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,7 @@ import zmaster587.advancedRocketry.api.DataStorage.DataType; import zmaster587.advancedRocketry.inventory.TextureResources; import zmaster587.advancedRocketry.inventory.modules.ModuleData; +import zmaster587.advancedRocketry.item.IDataItem; import zmaster587.advancedRocketry.item.ItemAsteroidChip; import zmaster587.advancedRocketry.item.ItemData; import zmaster587.advancedRocketry.tile.hatch.TileDataBus; @@ -37,22 +39,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 +95,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 +128,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 +332,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,22 +424,36 @@ 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) { @@ -330,18 +467,26 @@ public List getModules(int ID, EntityPlayer player) { 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 +502,6 @@ public List getModules(int ID, EntityPlayer player) { } } - for (int i = 0; i < finalList.size(); i++) { Asteroid asteroid = finalList.get(i); @@ -371,47 +515,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 +622,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,66 +639,129 @@ 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; } + + // --- server-side handlers only --- + if (!world.isRemote) { + 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) { - } else if (id == PROCESS_CHIP && !world.isRemote) { + // Keep printed set aligned with current seed + if (printedSetSeed != lastSeed) { + printedButtonsThisSeed.clear(); + printedSetSeed = lastSeed; + } - 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); + // 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; + } - extractData(1000, DataType.COMPOSITION, EnumFacing.UP, true); - extractData(1000, DataType.MASS, EnumFacing.UP, true); - useEnergy(500); + 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 +773,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 +801,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); + } + } + + // Write the final state back to the item once + dataItem.setData(dataChip, chipData.getData(), chipData.getDataType()); + } } if (world.isRemote) { @@ -653,23 +961,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 +999,7 @@ public void storeData(int id) { } } + @Override @Nullable public String getName() { @@ -709,6 +1032,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..7128f0605 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,126 @@ 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; + } + + + + // 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 +160,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/world/WorldServerNotMulti.java b/src/main/java/zmaster587/advancedRocketry/world/WorldServerNotMulti.java index cf3f46408..3614f1737 100644 --- a/src/main/java/zmaster587/advancedRocketry/world/WorldServerNotMulti.java +++ b/src/main/java/zmaster587/advancedRocketry/world/WorldServerNotMulti.java @@ -56,7 +56,9 @@ protected void saveLevel() throws MinecraftException { } public World init() { - super.init(); + // Removed super.init(): it recreates per-world managers (loot/adv/scoreboard/functions) + // will be reloaded per dimension, slow and breaks custom data. + // load weather data from NBT WorldInfoSavedData wi = (WorldInfoSavedData) perWorldStorage.getOrLoadData(WorldInfoSavedData.class, "WorldInfoSavedData"); if (wi == null) { diff --git a/src/main/java/zmaster587/advancedRocketry/world/provider/WorldProviderPlanet.java b/src/main/java/zmaster587/advancedRocketry/world/provider/WorldProviderPlanet.java index f71ef4689..64783da5e 100644 --- a/src/main/java/zmaster587/advancedRocketry/world/provider/WorldProviderPlanet.java +++ b/src/main/java/zmaster587/advancedRocketry/world/provider/WorldProviderPlanet.java @@ -147,13 +147,20 @@ public void updateWeather() { world.getWorldInfo().setRaining(true); } + // Clamp to avoid IllegalArgumentException in Random#nextInt(0 or negative) + final int thunderProlong = props.thunderProlongationLength > 0 ? props.thunderProlongationLength : 12000; + final int thunderStart = props.thunderStartLength > 0 ? props.thunderStartLength : 168000; + final int rainProlong = props.rainProlongationLength > 0 ? props.rainProlongationLength : 12000; + final int rainStart = props.rainStartLength > 0 ? props.rainStartLength : 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 +175,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 +189,7 @@ public void updateWeather() { } } + world.prevThunderingStrength = world.thunderingStrength; if (world.getWorldInfo().isThundering()) { 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/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..f48be615f 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 @@ -287,6 +306,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 +349,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 +382,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 +412,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 +455,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 +473,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 +482,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 +502,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 +542,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 +570,44 @@ 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.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 +619,801 @@ 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.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! + +# 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.atmanalyzer.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=Projeccts 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. + +# 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..e8bdeb143 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,1183 @@ 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=可以让盔甲形成密封 +enchantment.advancedrocketry.spacebreathing.desc=令盔甲部件形成气密密封 -machine.tooltip.smallplatepress=需要下面有两块黑曜石才能运作 +machine.tooltip.smallplatepress=需要下方两格处存在黑曜石才可工作 -msg.crystalliser.gravityTooHigh=重力不够低 -msg.observetory.scan.tooltip=扫描新的小行星,将消耗100个距离数据 +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.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.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=发射倒计时: +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.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=使用持久耐用的双锂动力源。可能比你的寿命还长。 + +# 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