From bd9d0a4eff2efe740130acc404241e4784006996 Mon Sep 17 00:00:00 2001 From: Chicken-Bomes Date: Tue, 29 Apr 2014 10:54:20 +1000 Subject: [PATCH] Initial Commit --- .gitignore | 2 + build/build.gradle | 115 +++ build/build.properties | 5 + build/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 51106 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + build/gradlew | 164 ++++ build/gradlew.bat | 90 ++ build/settings.gradle | 19 + dev/GuiContainer.java | 803 +++++++++++++++++ resources/assets/nei/asm/blocks.asm | 341 ++++++++ resources/assets/nei/lang/de_DE.lang | 159 ++++ resources/assets/nei/lang/en_US.lang | 168 ++++ resources/assets/nei/lang/ru_RU.lang | 159 ++++ resources/assets/nei/lang/tr_TR.lang | 159 ++++ resources/assets/nei/lang/zh_CN.lang | 140 +++ resources/assets/nei/lang/zh_TW.lang | 159 ++++ resources/assets/nei/textures/gui/inv.png | Bin 0 -> 3926 bytes resources/assets/nei/textures/gui/potion.png | Bin 0 -> 3990 bytes .../assets/nei/textures/gui/recipebg.png | Bin 0 -> 3680 bytes resources/assets/nei/textures/nei_sprites.png | Bin 0 -> 28790 bytes resources/nei_at.cfg | 11 + resources/neimod.info | 15 + src/codechicken/nei/Button.java | 81 ++ src/codechicken/nei/ButtonCycled.java | 13 + src/codechicken/nei/ClientHandler.java | 231 +++++ src/codechicken/nei/ContainerCreativeInv.java | 112 +++ .../nei/ContainerEnchantmentModifier.java | 322 +++++++ .../nei/ContainerPotionCreator.java | 162 ++++ .../nei/DefaultHighlightHandler.java | 50 ++ src/codechicken/nei/ExtendedCreativeInv.java | 109 +++ src/codechicken/nei/FastTransferManager.java | 338 ++++++++ .../nei/GuiEnchantmentModifier.java | 99 +++ .../nei/GuiExtendedCreativeInv.java | 56 ++ src/codechicken/nei/GuiNEIButton.java | 41 + src/codechicken/nei/GuiPotionCreator.java | 330 +++++++ src/codechicken/nei/HUDRenderer.java | 96 +++ src/codechicken/nei/Image.java | 17 + .../nei/InfiniteStackSizeHandler.java | 55 ++ src/codechicken/nei/InfiniteToolHandler.java | 38 + .../nei/InventoryCraftingDummy.java | 20 + src/codechicken/nei/ItemList.java | 370 ++++++++ src/codechicken/nei/ItemMobSpawner.java | 139 +++ src/codechicken/nei/ItemPanel.java | 381 ++++++++ src/codechicken/nei/ItemPanelStack.java | 29 + src/codechicken/nei/ItemQuantityField.java | 56 ++ src/codechicken/nei/ItemStackMap.java | 230 +++++ src/codechicken/nei/ItemStackSet.java | 35 + src/codechicken/nei/KeyManager.java | 52 ++ src/codechicken/nei/Label.java | 32 + src/codechicken/nei/LangProxy.java | 19 + src/codechicken/nei/LayoutManager.java | 811 ++++++++++++++++++ src/codechicken/nei/LayoutStyleDefault.java | 153 ++++ src/codechicken/nei/LayoutStyleMinecraft.java | 89 ++ src/codechicken/nei/LayoutStyleTMIOld.java | 127 +++ src/codechicken/nei/NEIActions.java | 95 ++ src/codechicken/nei/NEICPH.java | 249 ++++++ src/codechicken/nei/NEIChestGuiHandler.java | 29 + src/codechicken/nei/NEIClientConfig.java | 527 ++++++++++++ src/codechicken/nei/NEIClientUtils.java | 371 ++++++++ src/codechicken/nei/NEICompatibility.java | 5 + src/codechicken/nei/NEIController.java | 271 ++++++ .../nei/NEICreativeGuiHandler.java | 22 + src/codechicken/nei/NEIDummySlotHandler.java | 24 + src/codechicken/nei/NEIModContainer.java | 94 ++ src/codechicken/nei/NEISPH.java | 264 ++++++ src/codechicken/nei/NEIServerConfig.java | 285 ++++++ src/codechicken/nei/NEIServerUtils.java | 363 ++++++++ src/codechicken/nei/OffsetPositioner.java | 28 + src/codechicken/nei/PlayerSave.java | 111 +++ src/codechicken/nei/PopupInputHandler.java | 69 ++ src/codechicken/nei/PositionedStack.java | 119 +++ src/codechicken/nei/SaveLoadButton.java | 123 +++ src/codechicken/nei/SearchField.java | 59 ++ src/codechicken/nei/ServerHandler.java | 149 ++++ src/codechicken/nei/SlotOP.java | 10 + src/codechicken/nei/SpawnerRenderer.java | 97 +++ src/codechicken/nei/TMIUninstaller.java | 279 ++++++ src/codechicken/nei/TextField.java | 182 ++++ src/codechicken/nei/VisiblityData.java | 38 + src/codechicken/nei/Widget.java | 72 ++ src/codechicken/nei/WidgetZOrder.java | 18 + src/codechicken/nei/WorldOverlayRenderer.java | 222 +++++ src/codechicken/nei/api/API.java | 195 +++++ .../nei/api/DefaultOverlayRenderer.java | 44 + src/codechicken/nei/api/GuiInfo.java | 26 + src/codechicken/nei/api/IConfigureNEI.java | 13 + .../nei/api/IGuiContainerOverlay.java | 8 + .../nei/api/IHighlightHandler.java | 15 + .../nei/api/IInfiniteItemHandler.java | 16 + src/codechicken/nei/api/INEIGuiAdapter.java | 43 + src/codechicken/nei/api/INEIGuiHandler.java | 48 ++ src/codechicken/nei/api/INEIModeHandler.java | 6 + src/codechicken/nei/api/IOverlayHandler.java | 9 + .../nei/api/IRecipeOverlayRenderer.java | 9 + src/codechicken/nei/api/IStackPositioner.java | 14 + src/codechicken/nei/api/ItemInfo.java | 431 ++++++++++ src/codechicken/nei/api/LayoutStyle.java | 20 + src/codechicken/nei/api/NEIInfo.java | 29 + .../nei/api/TaggedInventoryArea.java | 48 ++ src/codechicken/nei/asm/NEICorePlugin.java | 42 + src/codechicken/nei/asm/NEITransformer.java | 180 ++++ src/codechicken/nei/config/ArrayDumper.java | 31 + src/codechicken/nei/config/ConfigSet.java | 42 + src/codechicken/nei/config/DataDumper.java | 160 ++++ .../nei/config/GuiHighlightTips.java | 149 ++++ src/codechicken/nei/config/GuiOptionList.java | 378 ++++++++ .../nei/config/IConfigSetHolder.java | 7 + .../nei/config/ItemPanelDumper.java | 114 +++ src/codechicken/nei/config/Option.java | 183 ++++ src/codechicken/nei/config/OptionButton.java | 146 ++++ src/codechicken/nei/config/OptionCycled.java | 66 ++ .../nei/config/OptionGamemodes.java | 25 + .../nei/config/OptionHighlightTips.java | 29 + src/codechicken/nei/config/OptionKeyBind.java | 95 ++ src/codechicken/nei/config/OptionList.java | 155 ++++ .../nei/config/OptionStringSet.java | 173 ++++ .../nei/config/OptionTextField.java | 110 +++ .../nei/config/OptionToggleButton.java | 43 + .../nei/config/OptionUtilities.java | 51 ++ .../nei/config/RegistryDumper.java | 31 + .../nei/guihook/DefaultSlotClickHandler.java | 28 + .../nei/guihook/GuiContainerManager.java | 545 ++++++++++++ .../nei/guihook/IContainerDrawHandler.java | 39 + .../nei/guihook/IContainerInputHandler.java | 85 ++ .../nei/guihook/IContainerObjectHandler.java | 51 ++ .../guihook/IContainerSlotClickHandler.java | 11 + .../nei/guihook/IContainerTooltipHandler.java | 40 + .../nei/guihook/IGuiClientSide.java | 8 + .../nei/guihook/IGuiHandleMouseWheel.java | 9 + src/codechicken/nei/guihook/IGuiSlotDraw.java | 9 + .../nei/recipe/BrewingOverlayHandler.java | 21 + .../nei/recipe/BrewingRecipeHandler.java | 220 +++++ .../nei/recipe/ContainerRecipe.java | 147 ++++ .../nei/recipe/DefaultOverlayHandler.java | 312 +++++++ .../nei/recipe/FireworkRecipeHandler.java | 175 ++++ .../nei/recipe/FuelRecipeHandler.java | 103 +++ .../nei/recipe/FurnaceRecipeHandler.java | 158 ++++ .../nei/recipe/GuiCraftingRecipe.java | 52 ++ src/codechicken/nei/recipe/GuiRecipe.java | 303 +++++++ .../nei/recipe/GuiUsageRecipe.java | 53 ++ .../nei/recipe/ICraftingHandler.java | 18 + .../nei/recipe/IRecipeHandler.java | 121 +++ src/codechicken/nei/recipe/IUsageHandler.java | 19 + .../nei/recipe/ProfilerRecipeHandler.java | 175 ++++ src/codechicken/nei/recipe/RecipeInfo.java | 119 +++ .../nei/recipe/RecipeItemInputHandler.java | 80 ++ .../nei/recipe/ShapedRecipeHandler.java | 212 +++++ .../nei/recipe/ShapelessRecipeHandler.java | 211 +++++ .../nei/recipe/TemplateRecipeHandler.java | 595 +++++++++++++ 149 files changed, 18486 insertions(+) create mode 100644 .gitignore create mode 100644 build/build.gradle create mode 100644 build/build.properties create mode 100644 build/gradle/wrapper/gradle-wrapper.jar create mode 100644 build/gradle/wrapper/gradle-wrapper.properties create mode 100644 build/gradlew create mode 100644 build/gradlew.bat create mode 100644 build/settings.gradle create mode 100644 dev/GuiContainer.java create mode 100644 resources/assets/nei/asm/blocks.asm create mode 100644 resources/assets/nei/lang/de_DE.lang create mode 100644 resources/assets/nei/lang/en_US.lang create mode 100644 resources/assets/nei/lang/ru_RU.lang create mode 100644 resources/assets/nei/lang/tr_TR.lang create mode 100644 resources/assets/nei/lang/zh_CN.lang create mode 100644 resources/assets/nei/lang/zh_TW.lang create mode 100644 resources/assets/nei/textures/gui/inv.png create mode 100644 resources/assets/nei/textures/gui/potion.png create mode 100644 resources/assets/nei/textures/gui/recipebg.png create mode 100644 resources/assets/nei/textures/nei_sprites.png create mode 100644 resources/nei_at.cfg create mode 100644 resources/neimod.info create mode 100644 src/codechicken/nei/Button.java create mode 100644 src/codechicken/nei/ButtonCycled.java create mode 100644 src/codechicken/nei/ClientHandler.java create mode 100644 src/codechicken/nei/ContainerCreativeInv.java create mode 100644 src/codechicken/nei/ContainerEnchantmentModifier.java create mode 100644 src/codechicken/nei/ContainerPotionCreator.java create mode 100644 src/codechicken/nei/DefaultHighlightHandler.java create mode 100644 src/codechicken/nei/ExtendedCreativeInv.java create mode 100644 src/codechicken/nei/FastTransferManager.java create mode 100644 src/codechicken/nei/GuiEnchantmentModifier.java create mode 100644 src/codechicken/nei/GuiExtendedCreativeInv.java create mode 100644 src/codechicken/nei/GuiNEIButton.java create mode 100644 src/codechicken/nei/GuiPotionCreator.java create mode 100644 src/codechicken/nei/HUDRenderer.java create mode 100644 src/codechicken/nei/Image.java create mode 100644 src/codechicken/nei/InfiniteStackSizeHandler.java create mode 100644 src/codechicken/nei/InfiniteToolHandler.java create mode 100644 src/codechicken/nei/InventoryCraftingDummy.java create mode 100644 src/codechicken/nei/ItemList.java create mode 100644 src/codechicken/nei/ItemMobSpawner.java create mode 100644 src/codechicken/nei/ItemPanel.java create mode 100644 src/codechicken/nei/ItemPanelStack.java create mode 100644 src/codechicken/nei/ItemQuantityField.java create mode 100644 src/codechicken/nei/ItemStackMap.java create mode 100644 src/codechicken/nei/ItemStackSet.java create mode 100644 src/codechicken/nei/KeyManager.java create mode 100644 src/codechicken/nei/Label.java create mode 100644 src/codechicken/nei/LangProxy.java create mode 100644 src/codechicken/nei/LayoutManager.java create mode 100644 src/codechicken/nei/LayoutStyleDefault.java create mode 100644 src/codechicken/nei/LayoutStyleMinecraft.java create mode 100644 src/codechicken/nei/LayoutStyleTMIOld.java create mode 100644 src/codechicken/nei/NEIActions.java create mode 100644 src/codechicken/nei/NEICPH.java create mode 100644 src/codechicken/nei/NEIChestGuiHandler.java create mode 100644 src/codechicken/nei/NEIClientConfig.java create mode 100644 src/codechicken/nei/NEIClientUtils.java create mode 100644 src/codechicken/nei/NEICompatibility.java create mode 100644 src/codechicken/nei/NEIController.java create mode 100644 src/codechicken/nei/NEICreativeGuiHandler.java create mode 100644 src/codechicken/nei/NEIDummySlotHandler.java create mode 100644 src/codechicken/nei/NEIModContainer.java create mode 100644 src/codechicken/nei/NEISPH.java create mode 100644 src/codechicken/nei/NEIServerConfig.java create mode 100644 src/codechicken/nei/NEIServerUtils.java create mode 100644 src/codechicken/nei/OffsetPositioner.java create mode 100644 src/codechicken/nei/PlayerSave.java create mode 100644 src/codechicken/nei/PopupInputHandler.java create mode 100644 src/codechicken/nei/PositionedStack.java create mode 100644 src/codechicken/nei/SaveLoadButton.java create mode 100644 src/codechicken/nei/SearchField.java create mode 100644 src/codechicken/nei/ServerHandler.java create mode 100644 src/codechicken/nei/SlotOP.java create mode 100644 src/codechicken/nei/SpawnerRenderer.java create mode 100644 src/codechicken/nei/TMIUninstaller.java create mode 100644 src/codechicken/nei/TextField.java create mode 100644 src/codechicken/nei/VisiblityData.java create mode 100644 src/codechicken/nei/Widget.java create mode 100644 src/codechicken/nei/WidgetZOrder.java create mode 100644 src/codechicken/nei/WorldOverlayRenderer.java create mode 100644 src/codechicken/nei/api/API.java create mode 100644 src/codechicken/nei/api/DefaultOverlayRenderer.java create mode 100644 src/codechicken/nei/api/GuiInfo.java create mode 100644 src/codechicken/nei/api/IConfigureNEI.java create mode 100644 src/codechicken/nei/api/IGuiContainerOverlay.java create mode 100644 src/codechicken/nei/api/IHighlightHandler.java create mode 100644 src/codechicken/nei/api/IInfiniteItemHandler.java create mode 100644 src/codechicken/nei/api/INEIGuiAdapter.java create mode 100644 src/codechicken/nei/api/INEIGuiHandler.java create mode 100644 src/codechicken/nei/api/INEIModeHandler.java create mode 100644 src/codechicken/nei/api/IOverlayHandler.java create mode 100644 src/codechicken/nei/api/IRecipeOverlayRenderer.java create mode 100644 src/codechicken/nei/api/IStackPositioner.java create mode 100644 src/codechicken/nei/api/ItemInfo.java create mode 100644 src/codechicken/nei/api/LayoutStyle.java create mode 100644 src/codechicken/nei/api/NEIInfo.java create mode 100644 src/codechicken/nei/api/TaggedInventoryArea.java create mode 100644 src/codechicken/nei/asm/NEICorePlugin.java create mode 100644 src/codechicken/nei/asm/NEITransformer.java create mode 100644 src/codechicken/nei/config/ArrayDumper.java create mode 100644 src/codechicken/nei/config/ConfigSet.java create mode 100644 src/codechicken/nei/config/DataDumper.java create mode 100644 src/codechicken/nei/config/GuiHighlightTips.java create mode 100644 src/codechicken/nei/config/GuiOptionList.java create mode 100644 src/codechicken/nei/config/IConfigSetHolder.java create mode 100644 src/codechicken/nei/config/ItemPanelDumper.java create mode 100644 src/codechicken/nei/config/Option.java create mode 100644 src/codechicken/nei/config/OptionButton.java create mode 100644 src/codechicken/nei/config/OptionCycled.java create mode 100644 src/codechicken/nei/config/OptionGamemodes.java create mode 100644 src/codechicken/nei/config/OptionHighlightTips.java create mode 100644 src/codechicken/nei/config/OptionKeyBind.java create mode 100644 src/codechicken/nei/config/OptionList.java create mode 100644 src/codechicken/nei/config/OptionStringSet.java create mode 100644 src/codechicken/nei/config/OptionTextField.java create mode 100644 src/codechicken/nei/config/OptionToggleButton.java create mode 100644 src/codechicken/nei/config/OptionUtilities.java create mode 100644 src/codechicken/nei/config/RegistryDumper.java create mode 100644 src/codechicken/nei/guihook/DefaultSlotClickHandler.java create mode 100644 src/codechicken/nei/guihook/GuiContainerManager.java create mode 100644 src/codechicken/nei/guihook/IContainerDrawHandler.java create mode 100644 src/codechicken/nei/guihook/IContainerInputHandler.java create mode 100644 src/codechicken/nei/guihook/IContainerObjectHandler.java create mode 100644 src/codechicken/nei/guihook/IContainerSlotClickHandler.java create mode 100644 src/codechicken/nei/guihook/IContainerTooltipHandler.java create mode 100644 src/codechicken/nei/guihook/IGuiClientSide.java create mode 100644 src/codechicken/nei/guihook/IGuiHandleMouseWheel.java create mode 100644 src/codechicken/nei/guihook/IGuiSlotDraw.java create mode 100644 src/codechicken/nei/recipe/BrewingOverlayHandler.java create mode 100644 src/codechicken/nei/recipe/BrewingRecipeHandler.java create mode 100644 src/codechicken/nei/recipe/ContainerRecipe.java create mode 100644 src/codechicken/nei/recipe/DefaultOverlayHandler.java create mode 100644 src/codechicken/nei/recipe/FireworkRecipeHandler.java create mode 100644 src/codechicken/nei/recipe/FuelRecipeHandler.java create mode 100644 src/codechicken/nei/recipe/FurnaceRecipeHandler.java create mode 100644 src/codechicken/nei/recipe/GuiCraftingRecipe.java create mode 100644 src/codechicken/nei/recipe/GuiRecipe.java create mode 100644 src/codechicken/nei/recipe/GuiUsageRecipe.java create mode 100644 src/codechicken/nei/recipe/ICraftingHandler.java create mode 100644 src/codechicken/nei/recipe/IRecipeHandler.java create mode 100644 src/codechicken/nei/recipe/IUsageHandler.java create mode 100644 src/codechicken/nei/recipe/ProfilerRecipeHandler.java create mode 100644 src/codechicken/nei/recipe/RecipeInfo.java create mode 100644 src/codechicken/nei/recipe/RecipeItemInputHandler.java create mode 100644 src/codechicken/nei/recipe/ShapedRecipeHandler.java create mode 100644 src/codechicken/nei/recipe/ShapelessRecipeHandler.java create mode 100644 src/codechicken/nei/recipe/TemplateRecipeHandler.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ccc5e8ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build/build +/build/.gradle diff --git a/build/build.gradle b/build/build.gradle new file mode 100644 index 00000000..65864a6e --- /dev/null +++ b/build/build.gradle @@ -0,0 +1,115 @@ +buildscript { + repositories { + mavenCentral() + mavenLocal() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' + } +} + +apply plugin: 'forge' + +group = "codechicken" // http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = "NotEnoughItems" + +// Define properties file +ext.configFile = file "build.properties" + +configFile.withReader { + // Load config. It shall from now be referenced as simply config or project.config + def prop = new Properties() + prop.load(it) + project.ext.config = new ConfigSlurper().parse prop +} +dependencies { + compile "codechicken:CodeChickenLib:${config.mc_version}-${config.ccl_version}:dev" + //compile "codechicken:CodeChickenCore:${config.mc_version}-${config.ccc_version}:dev" +} + +version = "${project.config.mod_version}." + System.getenv("BUILD_NUMBER") ?: "1" + +println config.mc_version + "-" + config.forge_version +// Setup the forge minecraft plugin data. Specify the preferred forge/minecraft version here +minecraft { + version = config.mc_version + "-" + config.forge_version +} + +sourceSets { + main { + def root = project.projectDir.parentFile + java { + srcDir new File(root, "src") + } + resources { + srcDir new File(root, "resources") + } + } +} + +processResources { + //redo task if any of these properties change + inputs.property "version", project.version + inputs.property "mc_version", config.mc_version + + // Replace properties in info files + from(sourceSets.main.resources.srcDirs) { + include '*.info' + expand 'version':project.version,'mc_version':config.mc_version + } + // Copy everything else + from(sourceSets.main.resources.srcDirs) { + include 'assets/**/*.*' + } +} + +task sourceJar(type: Jar) { + from sourceSets.main.allSource + classifier = 'src' + version = "${project.minecraft.version}-${project.version}" +} + +def commonManifest = { + attributes 'FMLCorePlugin': 'codechicken.nei.asm.NEICorePlugin' + attributes 'AccessTransformer': 'nei_at.cfg' +} + +jar { + classifier = 'universal' + version = "${project.minecraft.version}-${project.version}" + manifest commonManifest +} + +task devJar(type: Jar) { + from sourceSets.main.output + classifier = 'dev' + version = "${project.minecraft.version}-${project.version}" + manifest commonManifest +} + +// Tell the artifact system about our extra jars +artifacts { + archives sourceJar, devJar +} + +// Configure an upload task. +uploadArchives { + dependsOn 'reobf' + if (project.hasProperty("local_maven")) { + repositories { + logger.info('Publishing to local maven repo') + + mavenDeployer { + repository(url: "file://${local_maven}") + } + } + } +} \ No newline at end of file diff --git a/build/build.properties b/build/build.properties new file mode 100644 index 00000000..a9ad40b1 --- /dev/null +++ b/build/build.properties @@ -0,0 +1,5 @@ +mc_version=1.7.2 +forge_version=10.12.0.1054 +ccl_version=1.1.0.74 +ccc_version=1.0.0 +mod_version=1.0 diff --git a/build/gradle/wrapper/gradle-wrapper.jar b/build/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..3c7abdf12790879c06b07176de29647f77aa4129 GIT binary patch literal 51106 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

CflOl1i4(-2^1KXo)&I5QlgjRKFQgM zD6ehCWxkntKAc=>I3D4u%G}7e=qxAA?Sf`7*}AmHFeW@~qH!)52qnK%eE1Y#m6@67 zO3V-|xB*e9&pCv-V1+5(CZj28OXi|x%O;Z1nrRvV`va^-K+)hKm%358ZVl@hdM9FC z`qetqkt}(vC?B4YCb`J1(B|W2FUG9=weI5{@{Eh?>TQW{wfaYPWn!Jhvi4SDn*L$O z+ba3AEvl-&kMm{7T5kJbXBWyP97&!1W`(U0yLFAp9aCM&B={x zw*WRe*|v*CO#xJU;A^drAdD7ha@q#PMDU?H^H2WEu}hJ9kuKa2l$b+q&aPcCIBJZP zAZo7C9ZN3co+jwrzGvV{^s{n)Kc3W#5G$jqL7K|khz zHk9sIccAw2J>9kHTcA3D%3k#TKTv!LRIIO0y^=2-AV?H36JTji*0YMLNu)niMyk&E z>H$==7YOv~!yZRv+ZW0%4RLQvHEY1XN`DS6f_RM3L{@V~P819bgI?8PXV0;)N|M z_OCId;-W+3Nup|vCg}PkK!^wI7siD<`aYadbQJhMK)T2jHdK{cU2vw5dL!&%Od|^+ zWYfAf+WceYJw%7cLdinWYmJUeHjx+QXFw*q9snlQ7#m$U!&XcYZz3&bP|{nHH){)o z2oR$Xj=5F|89VqOZ{-3c&YDC#40G;G2J!EA1>VOXL_hTle3ZoE-^LmYnG|`3MDIzg zpD0HilUchX^S142{rYLEPrp_g1{{gWkr|HPP?SRBwD(v9W_))vD!Q&)ME8 zSqn$@K-gXj!KjW zE?pbiw!2Ea+NTTTYAi+aM_$J>(+K8|w5P|^h~B-Yz!OGn2=d8X+!g;So?07|^!WaL zG~pYy3zW9Cn_v8aRS1-}C#_q$CO(3MwoL5FsS7kld0qI)VlS6;X1*mdSP1 zf$sx2Bhc6b9k@Kibq*xVKTah~}u(zWjRCNOE`wS;aKjJk4K*^DTK@F45G5 zs1PuH;tY6CoP*^A`6iUj4WbjmhEkBPXCYx$O5^JFa7J0@i5stv( z5CV!l5pY>sFbST5=Lb{?BZh-*AO!6q1xfHspjn?W3ABKmv>}p?1@WK+)kX+3@s1F! z@a6z0$q3v-2$yQJ6@76nkN;wH%)hk}hW`wJ z{$~O#VQBZa)bMZg6RURVjI4_CW1D3%A$T89ap1KRfRJL-Fj+UN95AVdizybLu+xp5r`swfpn= zjvny!ra43xQ|=)wj4Z~IJzO5e&iY3B_zMix_<@1W9hr(uHCydIHB2oA#8IpkQgT+x zNiI09f?(F#1AA%lN(g#qU<6HPuq&yXoSvJ!4CO6uvq@+mjByDGIrJ*VVHS%S(`jS$syH!&2}e11N+vIh?Gegr%!V9Q znsd}fZ1@D1I1O2jrXk&3^rhMOaW9j|f3cpz?Es3cEJT}HwVs*DZN1%WScaR;$V{ZW z%Y~-hjEv3h$O4_ECgc)=xQalfgxl&E%1%;*H8ik=eoCA?96gEXG_zGy^AWXy!uh@! zb4Y5$!c2=YYPou!Y-v!_?PmKb;+MwWSFXgU0Y`<9nuc9V+C;__(Yex&NpHS^bZD@m zI!Bnb^yYKNv5V=liHdo3eo1x1c!(*Y72>=TYJhDGLLC4l^8_ZHeG8VUQzuE3^kZcZ z-AOK*YyQVZfmi(nr}(*p?x2ijn6|^2vB$Gf?Rr^iJ+z$Cue}Q|G3jS%W!x^oGxnM- z=f&|d&$K9NE+&H|8_STipg8m9q$i8>`otwi)sLO6{4x}mS`fcdgAOw_6$oytCN4Dw z=BCC8H+b&2>yXo>K`3(@BmZLljT$4t zF(STsM_l~MH;J*a_JRXs+`J%7pRhSsoPKnw-epH+r{2L;s@{cr+TNvmUOxp#>9P1X zNkNxu_>92imp-5#BxyMGrmb@vI&_WfjoJiYak4st&8YGRR%uv&Cgal*X3RLz?OqAr zCYRNQNr^G*rzv_@)~|f)G!2^!i5?=>LRg~my=+!y-(aZk6@p2N$#x2J5AD( zuz2=<&QyfjkY=S=8Yt~53@5u(a|C?f6t58*tEy9`-sZ$S1ZbE2rtT7~xZ?u%dZv#< z%OS~#Do{gG(O?`kF-u&!LwWFe``KTvFJ(Ag{hVufn6?_Bu`N6YNr-Bbvfi-lQkhBb zw_kZ5^rwn|+3W#X>k&|J>cj=oA z@hbF`1VMJSmk6TpEf&>00q}wk-x@+oPr@wmqS1F>K>l-Iq;C@tG4z5trKfu$_WFpI zZ*|+jd}qm73AYoxA>^s~^7I8M8<(4GC=H2pY^V#rUlFqMnr%HpULtphTKUAng9P=* zUokdOwgwK~D5NGY9(eSkM;c_*;HZAQDU$;y#BfZAZpN7$v(1kJzGYr~o8sF+6Gy)`+S(Q) zr+s}~x+LSp%Qp?^1+(DoM=ExNqF;)Z50aCwbAUZy-@!9a6naAy<`_KCIe7i8*e&H> zmjbP^=#|rDtd|(?>^`^&`vd+@muYuNFoXpT0N@A*06_MiU8aJei-n-Gv#G7oe>=() zwLiw2YN+48)>5m=Z7)jWO(Y$Y-CVCoN_D5Cx=@hDta%SeqLX8q>t!NU#dBy)y_z9o z*h2xaZMvaBNB_WL+PGP+L4A(ngJu&`x?NG){25Sx)ywmqb?<%LCjR=v|GEq0fc2B) zfKtNC5v>Y|WhcSnof^&rkBZ1;kKL_-e4h;hNxH-6X(np;xRgk6KxV&tV5mDB783jx z5+eWLZ+`ECl81C}37I!wUi6k7GIt2w{YErr7yX9B-$%2Lp|`hBP1H+uV6E6qVF*Ak zdhg2i4F*r&G^g(IGDFcjGG{M-pF`10z3=_Tci4_R0$=z>nAc5wP#XZ8JQ}5xJ5RH@ zoQkW>>;mW{x2npltVSc<0)o@Q!_CH+p_@r>VxCqjbJ`>w+OfX1Yzo*gfjucps;l;- z)F}Y>v?vPb%^YU89%V;QVJePVZ*S)I5ou#q>u04up%P{4x}!8hEfz}4!=9Pwr$b$J zMD&neYW+eAcpW(a3Rn=MNYeC`oLMW!nPR$a9!7SvuH?4!+BH z5!r?~n_YADL_{zzYajr)U^=2yhC;@qMbfs@Jj4PcHT0xL^dm^^@20Aa%#h>Z{k$Wb z3z&kA+vFqKpav>2Y}o5DtIdOhKymlE6J@0-C7ClXRcQ)+_83FsI>N~6O`Nm)&b}U= z#%_aVvDxAX2vp)}5x#o$5!HF3jMA`$prWl@gTcOX)md|qI^`na4v7?jKq%h)KJsdD z`I>lHnUkA0bDhM>%w?Z?$+go;c51ES86WFNm82c;y}fRs6M(S#3l0rtOh?f(d3cAU z2$7G_7$wa_XV{p?kAyfHf9j1RH?<*x+|&m|*(J^0EA<|^o5~oI+NDZcF@{^Kqdb$z zZ<39FXf86bIY$4^3Z?JYJ$3FERvi?_aiUT;C| z8j&CQ;p-dl_SfeyC!+tad-6}sQ8K;cd-P9Lfi&-8q5Z`}Ey}V@t4PJZS+F9HU_^CL z92kY5fZWlW>Y`08(d~P4`%#CJW~cE#lxM0n$G;OG`8KP0w|OmxGNUXC+S+#gMyj?w+Y zyOBnKWjn{Fq%M&IYL<95=T3*Ud!0yuNcOC`j;6T#3SNr+cU_%(y}j+m>tX|a3Ba_l z9Q_MH?t$gzo)}-D;f6Hztn6*?`4HULz1_)~WRiA8F*@urNZA4KU?yI+jjBTfz6S+A zOViz>$v_8zXEIt#DCUM%CEfAqY zuwgnoo?pw*W{uVU>~w{^%BKef(pOn6t81D9xEj91o6_95845@4*lQ;u-LI1NomHGv zi|(@xs$*NV9BN#N5s*n_$qH& z7B^ zxqxkE?Y<(`5XkPv8N++(%7yd(-AkU!NCTEgs-HXeqePOJ+m>8GwP6i$oGi>5QkFDS zfklKaq>X_7US|R8-AX|FdtQ*bBdVvtm&GOAqTI+IHV1uhvlTqk##pxX#-`knqA@f$ zdg8{xy*R9P#*2$LVm>`z1*`#I5{EFA8Do&EVX8v+USL(ZD|V_`Tx;NQT#&_E7jFI!`b;fCnS=q)qzzWb z#AOZ^R&Aj@^cb3O$gwZ$F!!M<&hE6mp#h^?kd@0r;N?39YFA%mi?}6EJe-m-`FUer z6rVr_Q*YBReUP4X(LgyD1ZL-SavES3{eERTHe%N&;mzvnT$Xxe6rDZ;L_v^oT5&)%0=b)jbKt9Va7oY zkdc)rnbq(^XVo+8vG^aL9AhyuB}O3z7x0CnON&jJk+5x5@+n?6C-`%$oxTavdscjI z*$26X-*YyXpNZhK66TT>pix}ntm$Kr2fdDln2GF}k~m=VpUMt~eYW9BjxfExh)cWiPl&?6%1`T1~X?7fM~1 znq`;Bc#~S?u*rG-Y`u0Zg@5eLhFNhM;R>IAi9f5;wx@bZ5WzWGr<>IiDe*n?GM ze`sfZBp!h^|L7+k`~W=(XLM9DP)-BVLDqvKU%@V#y+|IyHx33W(H-XxnhIVNvjbNb zo}xB3=!j7VcSlj9)T*>gwW@<#vaf*PxkU5D%F<3j>g59 z*$o!9ep;Wxr*uyT2ak>9vs! z&*<(kQ!&@#v>QgR|5?`IC{XbyaVM`H++Qv{4pAvb0f{J<`~KAp#?()oFI= zE4FCX*;1Y^zJ+&_&Qz+LYKCoQB%gfAG<1b9GP0BWekmh+n~uT~71U!YQ+(vT6~&m+ zb%flx&FJR;(6*#qA1B6&@W= ztBRMsjJ!c0c)An}jMP}nd5BpVjc*5IY7#w>j;>PMAM@vlU$h@F7iwD)WFsd414>rm zp`>URjgPz)6_neHMc}Tq7hz_Laha5FC1ml>eoIl-f9H2MieQ@0%pBO9a9XW6^^4$E z5|c3vX|DfxihVpPmlPfmOstV(J=rzf*@yrzRn2PjchS3c5SkeS50F zx3c44b67t_2iPcUl6VZrB60Hz3ma}|keQQ4a&n0xZ>e;MwkS<#tQ6C6G3|IXJzGHV zgtEfyB4Bf+@rY6rIn}UF#V{xEq&-E{m5=$`Q;6-1>DT@mmN++p&{rc7BdGawu}%Ga zOM5?uunCF1o(4BfkD~5F3Xuyeb(*uhusI~OgJ33M%VF4Y z!jQ4qWahGNe#N=(b)#%aUVfg+IrLMvRG-LP<&)w^x)fNB+WC-+AZhX~Ko@qW=6Hc! z%E2#%bG|6bts*D-SIRB=FTa%ABVeirIy*J%x*Ad5070P(UaGz{a6-3UH7NKB9+^3U z_u~XNhLrl)_FP#dnb)23dAL*c%Da=WqZ5ba<>dVk%Wy~fdRAh@-$>4DX6MPRl#H8r zH+eY&;dro{W*$%z)YWrV$!<1u-K1UiwYZ{mWBw)wETyV=`-+I4bSdx;7)$roP>Clw zAkfS>{_aTSJ`rPykk0+rtu(fB^HmRqUSh|@K5dhTn7GHrR9`_Fv>b*ci(%-Bw}KB{ ze_1Al1z5A<=?P^=WY3)@>oK^L_(#YBC#7R=O=S^Tf;_+oV-ndkHp@;pA8IR@7996x#LH@9QcOW#_t#C{f&e(z+t5o3KqLpmFo(9>y^HySTwX!D%EcHX+fC3}3O=OC4D)MzTj*rHat|TP1cfwHq{0DGQPWZ=gCN_OFJXJpW8&466THTA( z#Gp>iH2k4=>4QZ0=->n=y`oiAKb7P7J6tIK(uc#(kV*XGc*5UxIdl%76Vnpe1t)er z_uj6ft8v1Q-4WE$I>=byV8y$iaQbi*Thg@~5GA9fCGz2S&qpR)p2YBZ?$6ofIz$!D zxKmJB)Ek0VQ@u1`JFbG%&4CyzbtU$m+oE;WaAyg0m|O}dB7S{T zLoX?Lu0)j1N*7qJbC*m@yqG5OMp!MJA$?;CI&QZgf5dZ0bU+0?TR}1#0)PX-mR^h& zdez#|IQ6*+0n)YNTtCbm=c1ubk&!}MhQ;z|YsjA@wc^e7WyS?b-dJ6r%S;3p)}&9Q z$sXtOB6)2iOERZ6x~h)_*qT+Ut0I~qIEeKcMJzhu(6!sIo`?$VZ+Fzb$?C+Yq-aa^ zU7D~3JfG!1dTe?NBj~(<{L+~2{o5h|s7wq1dYrYB*z#hcvo97^4C<*A7jNqSFsY3| zv2l{`iG~R-N;O98FRzFPRTgt?N;p_g-Rvxnur$3#yzUvWo(cZNO?VbvH z5h;3AI_2*gDkrEgq&o>xuHVFNk2x(c4begN6|yeOq7`uw-6%vkr4g1``lK#VRL64h zjwL!1Ie4$mPt*-##hA^nhtzU>5Balr6`HaNQi5gkqD$1c?C^pq0ioa1{%a9rZIz@bjrJ^_3H9aV&1;OB;CEnxomgX7|-xI;|5K{+1S zC9*G~N(|C0TU(6+JNvC^}^FTG8uvP2>(Rp(8b-JBb zo{_&(6tsxrix#lNFA$rH9DeJn$Qv)qg_oznaci-5Z8d4ZayvCKd!Zmu3`_t&A$q|) z;gNePIeMKyPX8sl=&u8J#q08K^@^VpK{pscz(eR4*j(7*+j=^eF4xbi?pHkW3LUg# z?XA=JkMhc5(y+S!dbSH%%o~=_+00RG=B}{-SQhC?s`k2>Moxcc z1jpcy`|&vLggdkklBPV_1sc7iPkfyuQWe*t!bY=LLV%}VJc;;0wTkhe${HownLKHT zsB_KL8bvE_nZkaURn|_UKgue5A-6nqUT%=csb5K*ta)sP{nJ{MRfhZ6{K#~zU#y!b zx`CT`-A1Rd3Uqz`K) z8JxZqhB6;IJRe+~KcHh?|A#RBlM&;~9HB~nDL9`^e2&0~FZ|v)BI^{9nSSZdx$4y? zTHz_TLo|n5*rY=*?!X<1%r^q-eA!u9|2Id)WnNfxSN{+5Q!(MI$T0m-8D+S?s6%$_SkWg%;!_3BBM~gO=yiI@ z8(fW2SBZRsO9{D%SOy3} z98{3vD2sA292NqkOhnL{w;d=D@|@=5p>Cl*nLeO~DMai%VH*zzGi2Y~S`MPy$xLf> zou_)@2Xq4k^7(f=ha`yhc8MZHlbS9a9o%0>tYi~Y{d)++@UdMQ{63LZqRDFS96-7! z=XM59m(eJI{qbT@ztPUtfVP*8?cqF4FFeNk1js?I$my4$&|k=fC#}=!{FKsnsFMNB zQJ}irK(TPaQHJr*ToU*o&U6I)0p&UpT7LVPzyQSr1iuDb$x@Rz9!3$fkJK zRw3LTBb{hrEr7uiN zEksU#u#1_)pI=v|t6`CsL@f&0)8h-m{66{v_GQRO*uima4H3D{@AUG+m_Qp@4I=sO zEirmE4F3Ja|IciByI&@9_%D5z^0$fk|H3p2+1tA~yZoh_WeqLulwAy+T>d}qPE&hR z4S{#C5wsGi--Z#y0SF~)L{3=>JD&wIv>qeLAeE~)x}IK4B(k7fS_w_1~6_Jt4Lp3q# z6O*l>?if&-2Sdp)a7N52js2l7FP^=m@Mnz_gfxb~wMT2D-=;PO%7fs~5)SO~Z}lVL zW6y62qvCHGgXGT&?@roc=t)RQKt9Tu1?x*dJOy`Q0FI+FjDWF>GX~Th(`-$@mu+)M zzSA>Qo?%xO-+Bp9u61dt32>NeTv%)?D04*fv@X8+nhM=zmu5GbHPu*&?W$5|swDw; zX!N1Z;B7}PRlRaBixJR3mMxnT4$Wqz8aYo@^40ceJIXd20L$o@g)mEB;%Rjk6qx@YTg-0dNQJ1t1uM&-^a_i6ljzX;K5XByp z)LDD2B~xPVPMOivUUbmgLQ_qByw^0HTXFx%EnEk&n!nU}_YE$zGE)|15UABax>f6F zR&^osrW$)VDavKFk?Cl_SHSI4#S-JaJ2i+RvTv0b&>O|36kMDP(V43=hiyoqvm#AG z)KmBXrjz^KM7FI$S;UOFQW`FRw`o=Kf{3`qNXt}7pg|nZ3Xv;Xd+r0gdiL`h{`*m2 zk2ZGnvN?K@X8sD7E9@=^&GoEk;S_>rG_!lD<*)Z}rAY=S0P@(?B;bI8;-m^a0hFT+-?WdV}VSIodxM@#xDL^v)P{t#HU6MbD zL03b?Nr)tO$mpNs6~?z2MV}VB zU7~&u*Y{mxTzk6E#CK=E#6;T~z0RHCS|Zy!ReI{&gFl>oLiPr{uAUa&P4)Tb6jJZ^ zX_5E@-55W8I;sV_K|w;mBb+lhC%% zptY4mp9jS~x3h?ZZ5NQNL4BQ#)bdg^M}%@@QTaz9F8H-@XYygy5Uwr7B0A7z9H z_dD@nhN)XLtZnj+ZNFDKtSj{B8nIjW#C>wM>*!Jee zC%xu^B(rV0+ipEfPoaLerOpC-eRhA5&$gOg*_N%5rE#Z(Wm--%8r_?PT0A@~%B|NT zO@y=7Zu0b5M-1B?;I=x&(EAO1`+vy)Ktd2}3oca|Q-id)fZzY2aYF-7XfY3uH#d zdc7vobbMnIWsS!gg{H_gw|}21`^28XDXd3vfHbgGjo23lzLiRWqI$x8tBbwnl-EV* zrFh`1hL2M`?TD7QPSY!1(EutAU3466O2I+u5=&iBu8q4b=1H<1%4|U@?NFC5G8Kj* z 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%0DvTus6fLI&f~`9YxjD=W5pR41LBYQt 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 literal 0 HcmV?d00001 diff --git a/build/gradle/wrapper/gradle-wrapper.properties b/build/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..244a668f --- /dev/null +++ b/build/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Mar 26 13:33:58 CDT 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-bin.zip diff --git a/build/gradlew b/build/gradlew new file mode 100644 index 00000000..91a7e269 --- /dev/null +++ b/build/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/build/gradlew.bat b/build/gradlew.bat new file mode 100644 index 00000000..8a0b282a --- /dev/null +++ b/build/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/build/settings.gradle b/build/settings.gradle new file mode 100644 index 00000000..398e21f6 --- /dev/null +++ b/build/settings.gradle @@ -0,0 +1,19 @@ +/* + * This settings file was auto generated by the Gradle buildInit task + * by 'sunstrike' at '21/11/13 14:14' with Gradle 1.9 + * + * The settings file is used to specify which projects to include in your build. + * In a single project build this file can be empty or even removed. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user guide at http://gradle.org/docs/1.9/userguide/multi_project_builds.html + */ + +/* +// To declare projects as part of a multi-project build use the 'include' method +include 'shared' +include 'api' +include 'services:webservice' +*/ + +rootProject.name = 'CodeChickenLib' diff --git a/dev/GuiContainer.java b/dev/GuiContainer.java new file mode 100644 index 00000000..d61a57fe --- /dev/null +++ b/dev/GuiContainer.java @@ -0,0 +1,803 @@ +package net.minecraft.client.gui.inventory; + +import codechicken.nei.guihook.GuiContainerManager; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IIcon; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +@SideOnly(Side.CLIENT) +public abstract class GuiContainer extends GuiScreen +{ + protected static final ResourceLocation field_147001_a = new ResourceLocation("textures/gui/container/inventory.png"); + /** The X size of the inventory window in pixels. */ + public int xSize = 176; + /** The Y size of the inventory window in pixels. */ + public int ySize = 166; + /** A list of the players inventory slots */ + public Container inventorySlots; + /** + * Starting X position for the Gui. Inconsistent use for Gui backgrounds. + */ + public int guiLeft; + /** + * Starting Y position for the Gui. Inconsistent use for Gui backgrounds. + */ + public int guiTop; + private Slot theSlot; + /** Used when touchscreen is enabled. */ + private Slot clickedSlot; + /** Used when touchscreen is enabled. */ + private boolean isRightMouseClick; + /** Used when touchscreen is enabled */ + private ItemStack draggedStack; + private int field_147011_y; + private int field_147010_z; + private Slot returningStackDestSlot; + private long returningStackTime; + /** Used when touchscreen is enabled */ + private ItemStack returningStack; + private Slot field_146985_D; + private long field_146986_E; + protected final Set field_147008_s = new HashSet(); + protected boolean field_147007_t; + private int field_146987_F; + private int field_146988_G; + private boolean field_146995_H; + private int field_146996_I; + private long field_146997_J; + private Slot field_146998_K; + private int field_146992_L; + private boolean field_146993_M; + private ItemStack field_146994_N; + private static final String __OBFID = "CL_00000737"; + + public GuiContainerManager manager; + + public GuiContainer(Container par1Container) + { + this.inventorySlots = par1Container; + this.field_146995_H = true; + } + + /** + * Adds the buttons (and other controls) to the screen in question. + */ + public void initGui() + { + super.initGui(); + this.mc.thePlayer.openContainer = this.inventorySlots; + this.guiLeft = (this.width - this.xSize) / 2; + this.guiTop = (this.height - this.ySize) / 2; + } + + /** + * Draws the screen and all the components in it. + */ + public void drawScreen(int par1, int par2, float par3) + { + manager.preDraw(); + + this.drawDefaultBackground(); + int k = this.guiLeft; + int l = this.guiTop; + this.drawGuiContainerBackgroundLayer(par3, par1, par2); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + RenderHelper.disableStandardItemLighting(); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + super.drawScreen(par1, par2, par3); + RenderHelper.enableGUIStandardItemLighting(); + GL11.glPushMatrix(); + GL11.glTranslatef((float)k, (float)l, 0.0F); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + this.theSlot = null; + short short1 = 240; + short short2 = 240; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)short1 / 1.0F, (float)short2 / 1.0F); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + int k1; + + for (int i1 = 0; i1 < this.inventorySlots.inventorySlots.size(); ++i1) + { + Slot slot = (Slot)this.inventorySlots.inventorySlots.get(i1); + this.func_146977_a(slot); + + if (this.isMouseOverSlot(slot, par1, par2) && slot.func_111238_b() && !manager.objectUnderMouse(par1, par2)) + { + this.theSlot = slot; + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + int j1 = slot.xDisplayPosition; + k1 = slot.yDisplayPosition; + GL11.glColorMask(true, true, true, false); + this.drawGradientRect(j1, k1, j1 + 16, k1 + 16, -2130706433, -2130706433); + GL11.glColorMask(true, true, true, true); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + } + } + + //Forge: Force lighting to be disabled as there are some issue where lighting would + //incorrectly be applied based on items that are in the inventory. + GL11.glDisable(GL11.GL_LIGHTING); + this.drawGuiContainerForegroundLayer(par1, par2); + GL11.glEnable(GL11.GL_LIGHTING); + + manager.renderObjects(par1, par2); + + InventoryPlayer inventoryplayer = this.mc.thePlayer.inventory; + ItemStack itemstack = this.draggedStack == null ? inventoryplayer.getItemStack() : this.draggedStack; + + if (itemstack != null) + { + byte b0 = 8; + k1 = this.draggedStack == null ? 8 : 16; + String s = null; + + if (this.draggedStack != null && this.isRightMouseClick) + { + itemstack = itemstack.copy(); + itemstack.stackSize = MathHelper.ceiling_float_int((float)itemstack.stackSize / 2.0F); + } + else if (this.field_147007_t && this.field_147008_s.size() > 1) + { + itemstack = itemstack.copy(); + itemstack.stackSize = this.field_146996_I; + + if (itemstack.stackSize == 0) + { + s = "" + EnumChatFormatting.YELLOW + "0"; + } + } + + this.drawItemStack(itemstack, par1 - k - b0, par2 - l - k1, s); + } + + if (this.returningStack != null) + { + float f1 = (float)(Minecraft.getSystemTime() - this.returningStackTime) / 100.0F; + + if (f1 >= 1.0F) + { + f1 = 1.0F; + this.returningStack = null; + } + + k1 = this.returningStackDestSlot.xDisplayPosition - this.field_147011_y; + int j2 = this.returningStackDestSlot.yDisplayPosition - this.field_147010_z; + int l1 = this.field_147011_y + (int)((float)k1 * f1); + int i2 = this.field_147010_z + (int)((float)j2 * f1); + this.drawItemStack(this.returningStack, l1, i2, (String)null); + } + + GL11.glPopMatrix(); + /* + if (inventoryplayer.getItemStack() == null && this.theSlot != null && this.theSlot.getHasStack()) + { + ItemStack itemstack1 = this.theSlot.getStack(); + this.renderToolTip(itemstack1, par1, par2); + } + */ + manager.renderToolTips(par1, par2); + + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + RenderHelper.enableStandardItemLighting(); + } + + private void drawItemStack(ItemStack p_146982_1_, int p_146982_2_, int p_146982_3_, String p_146982_4_) + { + GL11.glTranslatef(0.0F, 0.0F, 32.0F); + this.zLevel = 500.0F; + itemRender.zLevel = 500.0F; + FontRenderer font = null; + if (p_146982_1_ != null) font = p_146982_1_.getItem().getFontRenderer(p_146982_1_); + if (font == null) font = fontRendererObj; + itemRender.renderItemAndEffectIntoGUI(font, this.mc.getTextureManager(), p_146982_1_, p_146982_2_, p_146982_3_); + itemRender.renderItemOverlayIntoGUI(font, this.mc.getTextureManager(), p_146982_1_, p_146982_2_, p_146982_3_ - (this.draggedStack == null ? 0 : 8), p_146982_4_); + this.zLevel = 0.0F; + itemRender.zLevel = 0.0F; + } + + /** + * Draw the foreground layer for the GuiContainer (everything in front of the items) + */ + protected void drawGuiContainerForegroundLayer(int p_146979_1_, int p_146979_2_) {} + + protected abstract void drawGuiContainerBackgroundLayer(float var1, int var2, int var3); + + private void func_146977_a(Slot p_146977_1_) + { + int i = p_146977_1_.xDisplayPosition; + int j = p_146977_1_.yDisplayPosition; + ItemStack itemstack = p_146977_1_.getStack(); + boolean flag = false; + boolean flag1 = p_146977_1_ == this.clickedSlot && this.draggedStack != null && !this.isRightMouseClick; + ItemStack itemstack1 = this.mc.thePlayer.inventory.getItemStack(); + String s = null; + + if (p_146977_1_ == this.clickedSlot && this.draggedStack != null && this.isRightMouseClick && itemstack != null) + { + itemstack = itemstack.copy(); + itemstack.stackSize /= 2; + } + else if (this.field_147007_t && this.field_147008_s.contains(p_146977_1_) && itemstack1 != null) + { + if (this.field_147008_s.size() == 1) + { + return; + } + + if (Container.func_94527_a(p_146977_1_, itemstack1, true) && this.inventorySlots.canDragIntoSlot(p_146977_1_)) + { + itemstack = itemstack1.copy(); + flag = true; + Container.func_94525_a(this.field_147008_s, this.field_146987_F, itemstack, p_146977_1_.getStack() == null ? 0 : p_146977_1_.getStack().stackSize); + + if (itemstack.stackSize > itemstack.getMaxStackSize()) + { + s = EnumChatFormatting.YELLOW + "" + itemstack.getMaxStackSize(); + itemstack.stackSize = itemstack.getMaxStackSize(); + } + + if (itemstack.stackSize > p_146977_1_.getSlotStackLimit()) + { + s = EnumChatFormatting.YELLOW + "" + p_146977_1_.getSlotStackLimit(); + itemstack.stackSize = p_146977_1_.getSlotStackLimit(); + } + } + else + { + this.field_147008_s.remove(p_146977_1_); + this.func_146980_g(); + } + } + + this.zLevel = 100.0F; + itemRender.zLevel = 100.0F; + + if (itemstack == null) + { + IIcon iicon = p_146977_1_.getBackgroundIconIndex(); + + if (iicon != null) + { + GL11.glDisable(GL11.GL_LIGHTING); + this.mc.getTextureManager().bindTexture(TextureMap.locationItemsTexture); + this.drawTexturedModelRectFromIcon(i, j, iicon, 16, 16); + GL11.glEnable(GL11.GL_LIGHTING); + flag1 = true; + } + } + + if (!flag1) + { + if (flag) + { + drawRect(i, j, i + 16, j + 16, -2130706433); + } + + GL11.glEnable(GL11.GL_DEPTH_TEST); + manager.renderSlotUnderlay(p_146977_1_); + manager.drawSlotItem(p_146977_1_, itemstack, i, j, s); + manager.renderSlotOverlay(p_146977_1_); + } + + itemRender.zLevel = 0.0F; + this.zLevel = 0.0F; + } + + private void func_146980_g() + { + ItemStack itemstack = this.mc.thePlayer.inventory.getItemStack(); + + if (itemstack != null && this.field_147007_t) + { + this.field_146996_I = itemstack.stackSize; + ItemStack itemstack1; + int i; + + for (Iterator iterator = this.field_147008_s.iterator(); iterator.hasNext(); this.field_146996_I -= itemstack1.stackSize - i) + { + Slot slot = (Slot)iterator.next(); + itemstack1 = itemstack.copy(); + i = slot.getStack() == null ? 0 : slot.getStack().stackSize; + Container.func_94525_a(this.field_147008_s, this.field_146987_F, itemstack1, i); + + if (itemstack1.stackSize > itemstack1.getMaxStackSize()) + { + itemstack1.stackSize = itemstack1.getMaxStackSize(); + } + + if (itemstack1.stackSize > slot.getSlotStackLimit()) + { + itemstack1.stackSize = slot.getSlotStackLimit(); + } + } + } + } + + /** + * Returns the slot at the given coordinates or null if there is none. + */ + public Slot getSlotAtPosition(int p_146975_1_, int p_146975_2_) + { + for (int k = 0; k < this.inventorySlots.inventorySlots.size(); ++k) + { + Slot slot = (Slot)this.inventorySlots.inventorySlots.get(k); + + if (this.isMouseOverSlot(slot, p_146975_1_, p_146975_2_)) + { + return slot; + } + } + + return null; + } + + /** + * Called when the mouse is clicked. + */ + protected void mouseClicked(int par1, int par2, int par3) + { + super.mouseClicked(par1, par2, par3); + + if (manager.mouseClicked(par1, par2, par3)) + return; + + boolean flag = par3 == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100; + Slot slot = this.getSlotAtPosition(par1, par2); + long l = Minecraft.getSystemTime(); + this.field_146993_M = this.field_146998_K == slot && l - this.field_146997_J < 250L && this.field_146992_L == par3; + this.field_146995_H = false; + + if (par3 == 0 || par3 == 1 || flag) + { + int i1 = this.guiLeft; + int j1 = this.guiTop; + boolean flag1 = par1 < i1 || par2 < j1 || par1 >= i1 + this.xSize || par2 >= j1 + this.ySize; + int k1 = -1; + + if (slot != null) + { + k1 = slot.slotNumber; + } + + if (flag1) + { + k1 = -999; + } + + if (this.mc.gameSettings.touchscreen && flag1 && this.mc.thePlayer.inventory.getItemStack() == null) + { + this.mc.displayGuiScreen((GuiScreen)null); + return; + } + + if (k1 != -1) + { + if (this.mc.gameSettings.touchscreen) + { + if (slot != null && slot.getHasStack()) + { + this.clickedSlot = slot; + this.draggedStack = null; + this.isRightMouseClick = par3 == 1; + } + else + { + this.clickedSlot = null; + } + } + else if (!this.field_147007_t) + { + if (this.mc.thePlayer.inventory.getItemStack() == null) + { + if (par3 == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100) + { + this.handleMouseClick(slot, k1, par3, 3); + } + else + { + boolean flag2 = k1 != -999 && (Keyboard.isKeyDown(42) || Keyboard.isKeyDown(54)); + byte b0 = 0; + + if (flag2) + { + this.field_146994_N = slot != null && slot.getHasStack() ? slot.getStack() : null; + b0 = 1; + } + else if (k1 == -999) + { + b0 = 4; + } + + manager.handleMouseClick(slot, k1, par3, b0); + } + + this.field_146995_H = true; + } + else + { + this.field_147007_t = true; + this.field_146988_G = par3; + this.field_147008_s.clear(); + + if (par3 == 0) + { + this.field_146987_F = 0; + } + else if (par3 == 1) + { + this.field_146987_F = 1; + } + } + } + } + } + + this.field_146998_K = slot; + this.field_146997_J = l; + this.field_146992_L = par3; + } + + /** + * Called when a mouse button is pressed and the mouse is moved around. Parameters are : mouseX, mouseY, + * lastButtonClicked & timeSinceMouseClick. + */ + protected void mouseClickMove(int p_146273_1_, int p_146273_2_, int p_146273_3_, long p_146273_4_) + { + Slot slot = this.getSlotAtPosition(p_146273_1_, p_146273_2_); + ItemStack itemstack = this.mc.thePlayer.inventory.getItemStack(); + + manager.mouseDragged(p_146273_1_, p_146273_2_, p_146273_3_, p_146273_4_); + + if (this.clickedSlot != null && this.mc.gameSettings.touchscreen) + { + if (p_146273_3_ == 0 || p_146273_3_ == 1) + { + if (this.draggedStack == null) + { + if (slot != this.clickedSlot) + { + this.draggedStack = this.clickedSlot.getStack().copy(); + } + } + else if (this.draggedStack.stackSize > 1 && slot != null && Container.func_94527_a(slot, this.draggedStack, false)) + { + long i1 = Minecraft.getSystemTime(); + + if (this.field_146985_D == slot) + { + if (i1 - this.field_146986_E > 500L) + { + this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, 0, 0); + this.handleMouseClick(slot, slot.slotNumber, 1, 0); + this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, 0, 0); + this.field_146986_E = i1 + 750L; + --this.draggedStack.stackSize; + } + } + else + { + this.field_146985_D = slot; + this.field_146986_E = i1; + } + } + } + } + else if (this.field_147007_t && slot != null && itemstack != null && itemstack.stackSize > this.field_147008_s.size() && Container.func_94527_a(slot, itemstack, true) && slot.isItemValid(itemstack) && this.inventorySlots.canDragIntoSlot(slot)) + { + this.field_147008_s.add(slot); + this.func_146980_g(); + } + } + + /** + * Called when the mouse is moved or a mouse button is released. Signature: (mouseX, mouseY, which) which==-1 is + * mouseMove, which==0 or which==1 is mouseUp + */ + protected void mouseMovedOrUp(int p_146286_1_, int p_146286_2_, int p_146286_3_) + { + if(manager.overrideMouseUp(p_146286_1_, p_146286_2_, p_146286_3_)) + return; + + Slot slot = this.getSlotAtPosition(p_146286_1_, p_146286_2_); + int l = this.guiLeft; + int i1 = this.guiTop; + boolean flag = p_146286_1_ < l || p_146286_2_ < i1 || p_146286_1_ >= l + this.xSize || p_146286_2_ >= i1 + this.ySize; + int j1 = -1; + + if (slot != null) + { + j1 = slot.slotNumber; + } + + if (flag) + { + j1 = -999; + } + + Slot slot1; + Iterator iterator; + + if (this.field_146993_M && slot != null && p_146286_3_ == 0 && this.inventorySlots.func_94530_a((ItemStack)null, slot)) + { + if (isShiftKeyDown()) + { + if (slot != null && slot.inventory != null && this.field_146994_N != null) + { + iterator = this.inventorySlots.inventorySlots.iterator(); + + while (iterator.hasNext()) + { + slot1 = (Slot)iterator.next(); + + if (slot1 != null && slot1.canTakeStack(this.mc.thePlayer) && slot1.getHasStack() && slot1.inventory == slot.inventory && Container.func_94527_a(slot1, this.field_146994_N, true)) + { + this.handleMouseClick(slot1, slot1.slotNumber, p_146286_3_, 1); + } + } + } + } + else + { + this.handleMouseClick(slot, j1, p_146286_3_, 6); + } + + this.field_146993_M = false; + this.field_146997_J = 0L; + } + else + { + if (this.field_147007_t && this.field_146988_G != p_146286_3_) + { + this.field_147007_t = false; + this.field_147008_s.clear(); + this.field_146995_H = true; + return; + } + + if (this.field_146995_H) + { + this.field_146995_H = false; + return; + } + + boolean flag1; + + if (this.clickedSlot != null && this.mc.gameSettings.touchscreen) + { + if (p_146286_3_ == 0 || p_146286_3_ == 1) + { + if (this.draggedStack == null && slot != this.clickedSlot) + { + this.draggedStack = this.clickedSlot.getStack(); + } + + flag1 = Container.func_94527_a(slot, this.draggedStack, false); + + if (j1 != -1 && this.draggedStack != null && flag1) + { + this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, p_146286_3_, 0); + this.handleMouseClick(slot, j1, 0, 0); + + if (this.mc.thePlayer.inventory.getItemStack() != null) + { + this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, p_146286_3_, 0); + this.field_147011_y = p_146286_1_ - l; + this.field_147010_z = p_146286_2_ - i1; + this.returningStackDestSlot = this.clickedSlot; + this.returningStack = this.draggedStack; + this.returningStackTime = Minecraft.getSystemTime(); + } + else + { + this.returningStack = null; + } + } + else if (this.draggedStack != null) + { + this.field_147011_y = p_146286_1_ - l; + this.field_147010_z = p_146286_2_ - i1; + this.returningStackDestSlot = this.clickedSlot; + this.returningStack = this.draggedStack; + this.returningStackTime = Minecraft.getSystemTime(); + } + + this.draggedStack = null; + this.clickedSlot = null; + } + } + else if (this.field_147007_t && !this.field_147008_s.isEmpty()) + { + this.handleMouseClick((Slot)null, -999, Container.func_94534_d(0, this.field_146987_F), 5); + iterator = this.field_147008_s.iterator(); + + while (iterator.hasNext()) + { + slot1 = (Slot)iterator.next(); + this.handleMouseClick(slot1, slot1.slotNumber, Container.func_94534_d(1, this.field_146987_F), 5); + } + + this.handleMouseClick((Slot)null, -999, Container.func_94534_d(2, this.field_146987_F), 5); + } + else if (this.mc.thePlayer.inventory.getItemStack() != null) + { + if (p_146286_3_ == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100) + { + this.handleMouseClick(slot, j1, p_146286_3_, 3); + } + else + { + flag1 = j1 != -999 && (Keyboard.isKeyDown(42) || Keyboard.isKeyDown(54)); + + if (flag1) + { + this.field_146994_N = slot != null && slot.getHasStack() ? slot.getStack() : null; + } + + manager.handleMouseClick(slot, j1, p_146286_3_, flag1 ? 1 : 0); + } + } + else if(p_146286_3_ >= 0) + manager.mouseUp(p_146286_1_, p_146286_2_, p_146286_3_); + } + + if (this.mc.thePlayer.inventory.getItemStack() == null) + { + this.field_146997_J = 0L; + } + + this.field_147007_t = false; + } + + /** + * Returns if the passed mouse position is over the specified slot. + */ + private boolean isMouseOverSlot(Slot p_146981_1_, int p_146981_2_, int p_146981_3_) + { + return this.func_146978_c(p_146981_1_.xDisplayPosition, p_146981_1_.yDisplayPosition, 16, 16, p_146981_2_, p_146981_3_); + } + + protected boolean func_146978_c(int p_146978_1_, int p_146978_2_, int p_146978_3_, int p_146978_4_, int p_146978_5_, int p_146978_6_) + { + int k1 = this.guiLeft; + int l1 = this.guiTop; + p_146978_5_ -= k1; + p_146978_6_ -= l1; + return p_146978_5_ >= p_146978_1_ - 1 && p_146978_5_ < p_146978_1_ + p_146978_3_ + 1 && p_146978_6_ >= p_146978_2_ - 1 && p_146978_6_ < p_146978_2_ + p_146978_4_ + 1; + } + + protected void handleMouseClick(Slot p_146984_1_, int p_146984_2_, int p_146984_3_, int p_146984_4_) + { + if (p_146984_1_ != null) + { + p_146984_2_ = p_146984_1_.slotNumber; + } + + manager.handleSlotClick(p_146984_2_, p_146984_3_, p_146984_4_); + } + + /** + * Fired when a key is typed. This is the equivalent of KeyListener.keyTyped(KeyEvent e). + */ + protected void keyTyped(char par1, int par2) + { + if(manager.lastKeyTyped(par2, par1)) + return; + + if (par2 == 1 || par2 == this.mc.gameSettings.keyBindInventory.getKeyCode()) + { + this.mc.thePlayer.closeScreen(); + } + + this.checkHotbarKeys(par2); + + if (this.theSlot != null && this.theSlot.getHasStack()) + { + if (par2 == this.mc.gameSettings.keyBindPickBlock.getKeyCode()) + { + this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, 0, 3); + } + else if (par2 == this.mc.gameSettings.keyBindDrop.getKeyCode()) + { + this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, isCtrlKeyDown() ? 1 : 0, 4); + } + } + } + + /** + * This function is what controls the hotbar shortcut check when you press a number key when hovering a stack. + */ + protected boolean checkHotbarKeys(int p_146983_1_) + { + if (this.mc.thePlayer.inventory.getItemStack() == null && this.theSlot != null) + { + for (int j = 0; j < 9; ++j) + { + if (p_146983_1_ == this.mc.gameSettings.keyBindsHotbar[j].getKeyCode()) + { + this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, j, 2); + return true; + } + } + } + + return false; + } + + /** + * Called when the screen is unloaded. Used to disable keyboard repeat events + */ + public void onGuiClosed() + { + if (this.mc.thePlayer != null) + { + this.inventorySlots.onContainerClosed(this.mc.thePlayer); + } + } + + /** + * Returns true if this GUI should pause the game when it is displayed in single-player + */ + public boolean doesGuiPauseGame() + { + return false; + } + + /** + * Called from the main game loop to update the screen. + */ + public void updateScreen() + { + super.updateScreen(); + manager.updateScreen(); + + if (!this.mc.thePlayer.isEntityAlive() || this.mc.thePlayer.isDead) + { + this.mc.thePlayer.closeScreen(); + } + } + + public void setWorldAndResolution(Minecraft mc, int i, int j) + { + super.setWorldAndResolution(mc, i, j); + if(mc.currentScreen == this) + { + manager = new GuiContainerManager(this); + manager.load(); + } + } + + public void handleKeyboardInput() + { + manager.handleKeyboardInput(); + } + + public void handleMouseInput() + { + super.handleMouseInput(); + manager.handleMouseWheel(); + } +} \ No newline at end of file diff --git a/resources/assets/nei/asm/blocks.asm b/resources/assets/nei/asm/blocks.asm new file mode 100644 index 00000000..3b6fe59a --- /dev/null +++ b/resources/assets/nei/asm/blocks.asm @@ -0,0 +1,341 @@ +list spawnerPlaced +ILOAD 2 +PUTSTATIC codechicken/nei/ItemMobSpawner.placedX:I +ILOAD 3 +PUTSTATIC codechicken/nei/ItemMobSpawner.placedY:I +ILOAD 4 +PUTSTATIC codechicken/nei/ItemMobSpawner.placedZ:I +RETURN + +list d_spawnerWorld +ACONST_NULL + +list spawnerWorld +ALOAD 0 +INVOKEVIRTUAL net/minecraft/tileentity/MobSpawnerBaseLogic.getSpawnerWorld()Lnet/minecraft/world/World; + +list n_commaFix +LDC * +INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; +INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; +ARETURN + +list commaFix +INVOKESTATIC codechicken/nei/NEIServerUtils.fixTrailingCommaList(Ljava/lang/StringBuilder;)Ljava/lang/StringBuilder; + +#begin GuiContainer patches + +list m_getManager +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ARETURN + +list m_setWorldAndResolution +ALOAD 0 +ALOAD 1 +ILOAD 2 +ILOAD 3 +INVOKESPECIAL net/minecraft/client/gui/GuiScreen.setWorldAndResolution (Lnet/minecraft/client/Minecraft;II)V +ALOAD 1 +GETFIELD net/minecraft/client/Minecraft.currentScreen : Lnet/minecraft/client/gui/GuiScreen; +ALOAD 0 +IF_ACMPNE LEND +ALOAD 0 +NEW codechicken/nei/guihook/GuiContainerManager +DUP +ALOAD 0 +INVOKESPECIAL codechicken/nei/guihook/GuiContainerManager. (Lnet/minecraft/client/gui/inventory/GuiContainer;)V +PUTFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.load ()V +LEND +RETURN + +list m_handleKeyboardInput +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.handleKeyboardInput ()V +RETURN + +list m_handleMouseInput +ALOAD 0 +INVOKESPECIAL net/minecraft/client/gui/GuiScreen.handleMouseInput ()V +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.handleMouseWheel ()V +RETURN + +list m_publicKeyTyped +ALOAD 0 +ILOAD 1 +ILOAD 2 +INVOKEVIRTUAL net/minecraft/client/gui/inventory/GuiContainer.func_73869_a(CI)V +RETURN + +list m_callKeyTyped +ALOAD 0 +ILOAD 1 +ILOAD 2 +INVOKEVIRTUAL net/minecraft/client/gui/inventory/GuiContainer.publicKeyTyped(CI)V +RETURN + +list preDraw +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.preDraw ()V + +list n_objectUnderMouse +INVOKEVIRTUAL net/minecraft/inventory/Slot.func_111238_b ()Z +IFEQ LSKIP + +list objectUnderMouse +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ILOAD 1 +ILOAD 2 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.objectUnderMouse (II)Z +IFNE LSKIP + +list n_renderObjects +INVOKEVIRTUAL net/minecraft/client/gui/inventory/GuiContainer.func_146979_b (II)V +SIPUSH 2896 +INVOKESTATIC org/lwjgl/opengl/GL11.glEnable (I)V + +list renderObjects +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ILOAD 1 +ILOAD 2 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.renderObjects (II)V + +list d_renderToolTip +ALOAD 8 +INVOKEVIRTUAL net/minecraft/entity/player/InventoryPlayer.func_70445_o ()Lnet/minecraft/item/ItemStack; +IFNONNULL LSKIP +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_147006_u : Lnet/minecraft/inventory/Slot; +IFNULL LSKIP +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_147006_u : Lnet/minecraft/inventory/Slot; +INVOKEVIRTUAL net/minecraft/inventory/Slot.func_75216_d ()Z +IFEQ LSKIP +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_147006_u : Lnet/minecraft/inventory/Slot; +INVOKEVIRTUAL net/minecraft/inventory/Slot.func_75211_c ()Lnet/minecraft/item/ItemStack; +ASTORE 10 +ALOAD 0 +ALOAD 10 +ILOAD 1 +ILOAD 2 +INVOKEVIRTUAL net/minecraft/client/gui/inventory/GuiContainer.func_146285_a (Lnet/minecraft/item/ItemStack;II)V +LSKIP + +list renderTooltips +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ILOAD 1 +ILOAD 2 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.renderToolTips (II)V + +list d_zLevel +LDC 200F + +list zLevel +LDC 500F + +list d_drawSlot +GETSTATIC net/minecraft/client/gui/inventory/GuiContainer.field_146296_j : Lnet/minecraft/client/renderer/entity/RenderItem; +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_146289_q : Lnet/minecraft/client/gui/FontRenderer; +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_146297_k : Lnet/minecraft/client/Minecraft; +INVOKEVIRTUAL net/minecraft/client/Minecraft.func_110434_K ()Lnet/minecraft/client/renderer/texture/TextureManager; +ALOAD 4 +ILOAD 2 +ILOAD 3 +INVOKEVIRTUAL net/minecraft/client/renderer/entity/RenderItem.func_82406_b (Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/item/ItemStack;II)V +GETSTATIC net/minecraft/client/gui/inventory/GuiContainer.field_146296_j : Lnet/minecraft/client/renderer/entity/RenderItem; +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_146289_q : Lnet/minecraft/client/gui/FontRenderer; +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_146297_k : Lnet/minecraft/client/Minecraft; +INVOKEVIRTUAL net/minecraft/client/Minecraft.func_110434_K ()Lnet/minecraft/client/renderer/texture/TextureManager; +ALOAD 4 +ILOAD 2 +ILOAD 3 +ALOAD 8 +INVOKEVIRTUAL net/minecraft/client/renderer/entity/RenderItem.func_77021_b (Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V + +list drawSlot +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ALOAD 1 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.renderSlotUnderlay (Lnet/minecraft/inventory/Slot;)V +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ALOAD 1 +ALOAD 4 +ILOAD 2 +ILOAD 3 +ALOAD 8 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.drawSlotItem (Lnet/minecraft/inventory/Slot;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ALOAD 1 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.renderSlotOverlay (Lnet/minecraft/inventory/Slot;)V + +list n_mouseClicked +INVOKESPECIAL net/minecraft/client/gui/GuiScreen.func_73864_a (III)V + +list mouseClicked +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ILOAD 1 +ILOAD 2 +ILOAD 3 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.mouseClicked (III)Z +IFEQ LCONT +RETURN +LCONT + +list d_handleMouseClick +ALOAD 0 +ALOAD 5 +ILOAD 11 +ILOAD 3 +ILOAD 13 +INVOKEVIRTUAL net/minecraft/client/gui/inventory/GuiContainer.func_146984_a (Lnet/minecraft/inventory/Slot;III)V + +list handleMouseClick +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ALOAD 5 +ILOAD 11 +ILOAD 3 +ILOAD 13 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.handleMouseClick (Lnet/minecraft/inventory/Slot;III)V + +list n_mouseDragged +ASTORE 7 + +list mouseDragged +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ILOAD 1 +ILOAD 2 +ILOAD 3 +LLOAD 4 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.mouseDragged (IIIJ)V + +list overrideMouseUp +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ILOAD 1 +ILOAD 2 +ILOAD 3 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.overrideMouseUp (III)Z +IFEQ LCONT +RETURN +LCONT + +list d_handleMouseClick2 +ALOAD 0 +ALOAD 4 +ILOAD 8 +ILOAD 3 +ILOAD 11 +IFEQ L0 +ICONST_1 +GOTO LCONT +L0 +ICONST_0 +LCONT +INVOKEVIRTUAL net/minecraft/client/gui/inventory/GuiContainer.handleMouseClick (Lnet/minecraft/inventory/Slot;III)V + +list handleMouseClick2 +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ALOAD 4 +ILOAD 8 +ILOAD 3 +ILOAD 11 +IFEQ L0 +ICONST_1 +GOTO LCONT +L0 +ICONST_0 +LCONT +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.handleMouseClick (Lnet/minecraft/inventory/Slot;III)V + +list n_mouseUpGoto +IFNULL LSTART +ILOAD 3 +ALOAD 0 + +list n_mouseUp +LSKIP +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_146297_k : Lnet/minecraft/client/Minecraft; +GETFIELD net/minecraft/client/Minecraft.field_71439_g : Lnet/minecraft/client/entity/EntityClientPlayerMP; +GETFIELD net/minecraft/client/entity/EntityClientPlayerMP.field_71071_by : Lnet/minecraft/entity/player/InventoryPlayer; +INVOKEVIRTUAL net/minecraft/entity/player/InventoryPlayer.func_70445_o ()Lnet/minecraft/item/ItemStack; +IFNONNULL L30 +ALOAD 0 +LCONST_0 +PUTFIELD net/minecraft/client/gui/inventory/GuiContainer.field_146997_J : J + +list mouseUp +GOTO LSKIP +LSTART +ILOAD 3 +IFLT LSKIP +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ILOAD 1 +ILOAD 2 +ILOAD 3 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.mouseUp (III)V + +list d_handleSlotClick +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_146297_k : Lnet/minecraft/client/Minecraft; +GETFIELD net/minecraft/client/Minecraft.field_71442_b : Lnet/minecraft/client/multiplayer/PlayerControllerMP; +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_147002_h : Lnet/minecraft/inventory/Container; +GETFIELD net/minecraft/inventory/Container.field_75152_c : I +ILOAD 2 +ILOAD 3 +ILOAD 4 +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.field_146297_k : Lnet/minecraft/client/Minecraft; +GETFIELD net/minecraft/client/Minecraft.field_71439_g : Lnet/minecraft/client/entity/EntityClientPlayerMP; +INVOKEVIRTUAL net/minecraft/client/multiplayer/PlayerControllerMP.func_78753_a (IIIILnet/minecraft/entity/player/EntityPlayer;)Lnet/minecraft/item/ItemStack; +POP + +list handleSlotClick +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ILOAD 2 +ILOAD 3 +ILOAD 4 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.handleSlotClick (III)V + +list lastKeyTyped +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +ILOAD 2 +ILOAD 1 +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.lastKeyTyped (IC)Z +IFEQ LCONT +RETURN +LCONT + +list n_updateScreen +ALOAD 0 +INVOKESPECIAL net/minecraft/client/gui/GuiScreen.updateScreen ()V + +list updateScreen +ALOAD 0 +GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager; +INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.updateScreen ()V \ No newline at end of file diff --git a/resources/assets/nei/lang/de_DE.lang b/resources/assets/nei/lang/de_DE.lang new file mode 100644 index 00000000..84b74c34 --- /dev/null +++ b/resources/assets/nei/lang/de_DE.lang @@ -0,0 +1,159 @@ +load=Lade +save=Speichere + +enchant=Verzaubern +enchant.level=Level +enchant.locked=Gesperrt +enchant.unlocked=Entsperrt + +inventory.options=Optionen +inventory.prev=Zurück +inventory.next=Weiter +inventory.item_subsets=Item-SubSets +inventory.delete.all=LÖSCHE ALLE ITEMS aus deinem aktuellen Inventarbildschirm +inventory.delete.enable=Ermögliche Lösch-Modus +inventory.delete.1=Schalte Lösch-Modus an +inventory.delete.0=Schalte Lösch-Modus aus +inventory.rain.enable=Ermögliche Regen +inventory.rain.1=Schalte Regen an +inventory.rain.0=Schalte Regen aus +inventory.magnet.enable=Ermögliche Magnetmodus +inventory.magnet.1=Schalte Magnetmodus an +inventory.magnet.0=Schalte Magnetmodus aus +inventory.dawn.enable=Ermögliche Morgen-Mittag +inventory.dawn.set=Setze Zeit auf Morgen +inventory.noon.enable=Ermögliche Mittag-Abend +inventory.noon.set=Setze Zeit auf Mittag +inventory.dusk.enable=Ermögliche Abend-Nacht +inventory.dusk.set=Setze Zeit auf Abend +inventory.midnight.enable=Ermögliche Nacht-Morgen +inventory.midnight.set=Setze Zeit auf Nacht +inventory.heal=Heile den Spieler +inventory.gamemode.0=Setze Spielmodus auf Überleben +inventory.gamemode.1=Setze Spielmodus auf Kreativ +inventory.gamemode.2=Setze Spielmodus auf Kreativ+ +inventory.gamemode.3=Setze Spielmodus auf Abenteuer + +chat.gamemode.0=Du spielst nun Überleben +chat.gamemode.1=Du spielst nun Kreativ +chat.gamemode.2=Du spielst nun Kreativ+ +chat.gamemode.3=Du spielst nun Abenteuer + +options=NEI-Optionen +options.back=Zurück +options.world=Welt +options.global=Global +options.global.tip.0=Optionen werden auf alle Welten angewandt +options.global.tip.1=Clicke W neben einer:Option , um das Übergehen: für diese Welt zu aktivieren +options.wbutton.tip.0=Nutzt globale Einstellungen +options.wbutton.tip.1=Übergeht globale Einstellungen + +options.keys=Tastenbelegung +options.keys.gui=Inventar +options.keys.gui.recipe=Rezept +options.keys.gui.usage=Nutzung +options.keys.gui.enchant=Verzauberungen +options.keys.gui.potion=Tränke +options.keys.gui.back=Vorheriges Rezept +options.keys.gui.prev=Vorherige Seite +options.keys.gui.next=Nächste Seite +options.keys.gui.hide=Verstelle NEI-Sichtbarkeit +options.keys.gui.search=Fokussiere Such-Leiste +options.keys.world=Welt +options.keys.world.chunkoverlay=Chunk-Grenzen-Overlay +options.keys.world.moboverlay=Mob-Spawn-Overlay +options.keys.world.highlight_tips=Highlight-Tooltips +options.keys.world.dawn=Morgen +options.keys.world.noon=Mittag +options.keys.world.dusk=Abend +options.keys.world.midnight=Nacht +options.keys.world.rain=Regen +options.keys.world.heal=Heilen +options.keys.world.creative=Spielmodus +options.keys.world.creative.tip=Wechsle Spielmodus + +options.inventory=Inventar +options.inventory.widgetsenabled.true=NEI Aktiviert +options.inventory.widgetsenabled.false=NEI Deaktiviert +options.inventory.cheatmode.0=Rezeptmodus +options.inventory.cheatmode.1=Mehrzweckmodus +options.inventory.cheatmode.2=Cheat-Modus +options.inventory.layoutstyle=Layout-Stil +options.inventory.profileRecipes=Profil-Rezepte +options.inventory.profileRecipes.true=Wahr +options.inventory.profileRecipes.false=Falsch +options.inventory.itemIDs=Item-IDs +options.inventory.itemIDs.0=Versteckt +options.inventory.itemIDs.1=Auto +options.inventory.itemIDs.2=Gezeigt +options.inventory.utilities=Nützliches +options.inventory.utilities.tip=Wähle Inhalte für den Mehrzweckmodus +options.inventory.utilities.time=Zeit +options.inventory.utilities.rain=Regen +options.inventory.utilities.heal=Heilen +options.inventory.utilities.delete=Löschem +options.inventory.utilities.magnet=Magnet +options.inventory.utilities.gamemode=Spielmodus +options.inventory.utilities.enchant=Verzaubern +options.inventory.utilities.potion=Tränke +options.inventory.utilities.item=Items +options.inventory.gamemodes=Spielmodi +options.inventory.gamemodes.tip=Wähle die Spielmodi, zu denen gewechselt werden kann. +options.inventory.gamemodes.creative=Kreativ +options.inventory.gamemodes.creative+=Kreativ+ +options.inventory.gamemodes.adventure=Abenteuer + +options.command=Befehle +options.command.creative=Spielmodus +options.command.creative.tip={0} = 1 für Kreativ, 0 für Überleben, {1} = Spieler +options.command.item=Item +options.command.item.tip={0} = Spieler, {1} = itemID, {2} = Anzahl, {3} = itemDamage +options.command.time=Zeit +options.command.time.tip={0} = Weltzeit +options.command.rain=Regen +options.command.rain.tip={0} = 1 für An, 0 für Aus +options.command.heal=Heilen +options.command.heal.tip={0} = Spieler + +options.world.highlight_tips=Highlight-Tooltips +options.world.highlight_tips.hide=Versteckt +options.world.highlight_tips.show=Gezeigt + +options.tools=Werkzeuge +options.tools.dump=Daten-Ablage +options.tools.dump.dump=Ablegen +options.tools.dump.dumped=%s-IDs wurden nach %s abgelegt +options.tools.dump.mode.0=Genutzt +options.tools.dump.mode.1=Frei +options.tools.dump.mode.2=Alles +options.tools.dump.item=Block/Item +options.tools.dump.items=Blöcke/Items +options.tools.dump.potion=Trank +options.tools.dump.potions=Tränke +options.tools.dump.enchantment=Verzauberungs +options.tools.dump.enchantments=Verzauberungen +options.tools.dump.biome=Biom +options.tools.dump.biomes=Biome +options.tools.dump.itempanel=Item-Panel +options.tools.dump.itempanel.dumped=Inhalte des Item-Panels wurden nach %s abgelegt +options.tools.dump.itempanel.mode.0=CSV +options.tools.dump.itempanel.mode.1=NBT + +layoutstyle.minecraft=Minecraft +layoutstyle.oldtmi=Altes TMI + +recipe.page=Seite %s/%s +recipe.shaped=Geformte Herstellung +recipe.shapeless=Formlose Herstellung +recipe.furnace=Schmelzen +recipe.fuel=Brennstoff +recipe.fuel.required=%s erfordert +recipe.fuel.processed=%s verarbeitet +recipe.fuel.produced=%s produziert +recipe.firework=Feuerwerk +recipe.firework.tooltip0=Farbstoffe fügen den Ladungen Farben hinzu +recipe.firework.tooltip1=Neu zu färben fügt der Ladung verblassende Farben hinzu +recipe.firework.tooltip2=Mehere Ladungen können von einer Rakete gespawnt werden +recipe.brewing=Brauen +recipe.profiler.crafting=Herstellungsprofil +recipe.profiler.usage=Nutzungsprofil \ No newline at end of file diff --git a/resources/assets/nei/lang/en_US.lang b/resources/assets/nei/lang/en_US.lang new file mode 100644 index 00000000..4cc1babe --- /dev/null +++ b/resources/assets/nei/lang/en_US.lang @@ -0,0 +1,168 @@ +nei.load=Load +nei.save=Save + +nei.enchant=Enchant +nei.enchant.level=Level +nei.enchant.locked=Locked +nei.enchant.unlocked=Unlocked + +nei.inventory.options=Options +nei.inventory.prev=Prev +nei.inventory.next=Next +nei.inventory.item_subsets=Item SubSets +nei.inventory.delete.all=DELETE ALL ITEMS from current inventory screen +nei.inventory.delete.one=Delete %s +nei.inventory.delete.all=Delete ALL %s +nei.inventory.delete.enable=Enable delete mode +nei.inventory.delete.1=Turn delete mode on +nei.inventory.delete.0=Turn delete mode off +nei.inventory.rain.enable=Enable rain +nei.inventory.rain.1=Turn rain on +nei.inventory.rain.0=Turn rain off +nei.inventory.magnet.enable=Enable magnet mode +nei.inventory.magnet.1=Turn magnet mode on +nei.inventory.magnet.0=Turn magnet mode off +nei.inventory.dawn.enable=Enable dawn-noon +nei.inventory.dawn.set=Set time to dawn +nei.inventory.noon.enable=Enable noon-dusk +nei.inventory.noon.set=Set time to noon +nei.inventory.dusk.enable=Enable dusk-midnight +nei.inventory.dusk.set=Set time to dusk +nei.inventory.midnight.enable=Enable midnight-dawn +nei.inventory.midnight.set=Set time to midnight +nei.inventory.heal=Heal the player +nei.inventory.gamemode.0=Set gamemode to survival +nei.inventory.gamemode.1=Set gamemode to creative +nei.inventory.gamemode.2=Set gamemode to creative+ +nei.inventory.gamemode.3=Set gamemode to adventure + +nei.chat.gamemode.0=You are now playing survival +nei.chat.gamemode.1=You are now playing creative +nei.chat.gamemode.2=You are now playing creative+ +nei.chat.gamemode.3=You are now playing adventure +nei.chat.rain.on=Rain turned on +nei.chat.rain.off=Rain turned off +nei.chat.time=Day %d. %d:00 +nei.chat.give.noitem=No such item. + +nei.options=NEI Options +nei.options.back=Back +nei.options.world=World +nei.options.global=Global +nei.options.global.tip.0=Options apply to all worlds +nei.options.global.tip.1=Click the W beside an:option to enable an:override for this world +nei.options.wbutton.tip.0=Using global settings +nei.options.wbutton.tip.1=Overriding global setting + +nei.options.keys=KeyBindings +nei.options.keys.gui=Inventory +nei.options.keys.gui.recipe=Recipe +nei.options.keys.gui.usage=Usage +nei.options.keys.gui.enchant=Enchantments +nei.options.keys.gui.potion=Potions +nei.options.keys.gui.back=Prev Recipe +nei.options.keys.gui.prev=Prev Page +nei.options.keys.gui.next=Next Page +nei.options.keys.gui.hide=Toggle NEI Visibility +nei.options.keys.gui.search=Focus Search Bar +nei.options.keys.world=World +nei.options.keys.world.chunkoverlay=Chunk Boundary Overlay +nei.options.keys.world.moboverlay=Mob Spawn Overlay +nei.options.keys.world.highlight_tips=Highlight Tooltips +nei.options.keys.world.dawn=Dawn +nei.options.keys.world.noon=Noon +nei.options.keys.world.dusk=Dusk +nei.options.keys.world.midnight=Midnight +nei.options.keys.world.rain=Rain +nei.options.keys.world.heal=Heal +nei.options.keys.world.creative=Gamemode +nei.options.keys.world.creative.tip=Cycle Gamemode + +nei.options.inventory=Inventory +nei.options.inventory.widgetsenabled.true=NEI Enabled +nei.options.inventory.widgetsenabled.false=NEI Disabled +nei.options.inventory.cheatmode.0=Recipe Mode +nei.options.inventory.cheatmode.1=Utility Mode +nei.options.inventory.cheatmode.2=Cheat Mode +nei.options.inventory.layoutstyle=Layout Style +nei.options.inventory.profileRecipes=Profile Recipes +nei.options.inventory.profileRecipes.true=True +nei.options.inventory.profileRecipes.false=False +nei.options.inventory.itemIDs=Item IDs +nei.options.inventory.itemIDs.0=Hidden +nei.options.inventory.itemIDs.1=Auto +nei.options.inventory.itemIDs.2=Shown +nei.options.inventory.utilities=Utilities +nei.options.inventory.utilities.tip=Select features for utility mode +nei.options.inventory.utilities.time=Time +nei.options.inventory.utilities.rain=Rain +nei.options.inventory.utilities.heal=Heal +nei.options.inventory.utilities.delete=Delete +nei.options.inventory.utilities.magnet=Magnet +nei.options.inventory.utilities.gamemode=Gamemode +nei.options.inventory.utilities.enchant=Enchanting +nei.options.inventory.utilities.potion=Potions +nei.options.inventory.utilities.item=Items +nei.options.inventory.gamemodes=Gamemodes +nei.options.inventory.gamemodes.tip=Select the gamemodes to cycle through +nei.options.inventory.gamemodes.creative=Creative +nei.options.inventory.gamemodes.creative+=Creative+ +nei.options.inventory.gamemodes.adventure=Adventure + +nei.options.command=Commands +nei.options.command.creative=Gamemode +nei.options.command.creative.tip={0} = 1 for creative, 0 for survival, {1} = player +nei.options.command.item=Item +nei.options.command.item.tip={0} = player, {1} = itemID, {2} = quantity, {3} = itemDamage +nei.options.command.time=Time +nei.options.command.time.tip={0} = worldtime +nei.options.command.rain=Rain +nei.options.command.rain.tip={0} = 1 for on, 0 for off +nei.options.command.heal=Heal +nei.options.command.heal.tip={0} = player + +nei.options.world.highlight_tips=Highlight Tooltips +nei.options.world.highlight_tips.hide=Hidden +nei.options.world.highlight_tips.show=Shown + +nei.options.tools=Tools +nei.options.tools.dump=Data Dumps +nei.options.tools.dump.dump=Dump +nei.options.tools.dump.dumped=%s IDs dumped to %s +nei.options.tools.dump.mode.0=Used +nei.options.tools.dump.mode.1=Free +nei.options.tools.dump.mode.2=All +nei.options.tools.dump.item=Item +nei.options.tools.dump.items=Items +nei.options.tools.dump.block=Block +nei.options.tools.dump.blocks=Blocks +nei.options.tools.dump.potion=Potion +nei.options.tools.dump.potions=Potions +nei.options.tools.dump.enchantment=Enchantment +nei.options.tools.dump.enchantments=Enchantments +nei.options.tools.dump.biome=Biome +nei.options.tools.dump.biomes=Biomes +nei.options.tools.dump.itempanel=Item Panel +nei.options.tools.dump.itempanel.dumped=Item Panel contents were dumped to %s +nei.options.tools.dump.itempanel.mode.0=CSV +nei.options.tools.dump.itempanel.mode.1=NBT +nei.options.tools.dump.itempanel.mode.2=Json + +nei.layoutstyle.minecraft=Minecraft +nei.layoutstyle.oldtmi=Old TMI + +nei.recipe.page=Page %s/%s +nei.recipe.shaped=Shaped Crafting +nei.recipe.shapeless=Shapeless Crafting +nei.recipe.furnace=Smelting +nei.recipe.fuel=Fuel +nei.recipe.fuel.required=%s required +nei.recipe.fuel.processed=%s processed +nei.recipe.fuel.produced=%s produced +nei.recipe.firework=Fireworks +nei.recipe.firework.tooltip0=Dyes add colours to the charges +nei.recipe.firework.tooltip1=Recolouring adds fade colours to the charge +nei.recipe.firework.tooltip2=Multiple charges can be spawned from one rocket +nei.recipe.brewing=Brewing +nei.recipe.profiler.crafting=Crafting Profiling +nei.recipe.profiler.usage=Usage Profiling \ No newline at end of file diff --git a/resources/assets/nei/lang/ru_RU.lang b/resources/assets/nei/lang/ru_RU.lang new file mode 100644 index 00000000..e6b13169 --- /dev/null +++ b/resources/assets/nei/lang/ru_RU.lang @@ -0,0 +1,159 @@ +load=Load +save=Save + +enchant=Зачаровывание +enchant.level=Уровень +enchant.locked=Открытые +enchant.unlocked=Закрытые + +inventory.options=Опции +inventory.prev=Предыдущая +inventory.next=Следующая +inventory.item_subsets=Подмножества предметов +inventory.delete.all=УДАЛИТЬ ВСЕ ПРЕДМЕТЫ из текущего инвентаря +inventory.delete.enable=Режим удаления +inventory.delete.1=Включить режим удаления +inventory.delete.0=Выключить режим удаления +inventory.rain.enable=Дождь +inventory.rain.1=Включить дождь +inventory.rain.0=Выключить дождь +inventory.magnet.enable=Режим магнита +inventory.magnet.1=Включить режим магнита +inventory.magnet.0=Выключить режим магнита +inventory.dawn.enable=Enable dawn-noon +inventory.dawn.set=Установить время на рассвет +inventory.noon.enable=Enable noon-dusk +inventory.noon.set=Установить время на полдень +inventory.dusk.enable=Enable dusk-midnight +inventory.dusk.set=Установить время на сумерки +inventory.midnight.enable=Enable midnight-dawn +inventory.midnight.set=Установить время на полночь +inventory.heal=Исцелить игрока +inventory.gamemode.0=Установить игровой режим на выживание +inventory.gamemode.1=Установить игровой режим на творческий +inventory.gamemode.2=Установить игровой режим на творческий+ +inventory.gamemode.3=Установить игровой режим на приключение + +chat.gamemode.0=Теперь вы играете в режиме выживание +chat.gamemode.1=Теперь вы играете в режиме творчество +chat.gamemode.2=Теперь вы играете в режиме творчество+ +chat.gamemode.3=Теперь вы играете в режиме приключение + +options=Опции NEI +options.back=Назад +options.world=Мировые +options.global=Глобальные +options.global.tip.0=Настройки применяются ко всем мирам +options.global.tip.1=Нажмите W рядом с:опцией, чтобы включить:перезапись для этого мира +options.wbutton.tip.0=Использование глобальных настроек +options.wbutton.tip.1=Перезаписывание глобальных настроек + +options.keys=Назначения клавиш +options.keys.gui=Инвентарь +options.keys.gui.recipe=Рецепт +options.keys.gui.usage=Использование +options.keys.gui.enchant=Чары +options.keys.gui.potion=Зелья +options.keys.gui.back=Предыдущий рецепт +options.keys.gui.prev=Предыдущая страница +options.keys.gui.next=Следующая страница +options.keys.gui.hide=Переключить видимость NEI +options.keys.gui.search=Сфокусироваться на панель поиска +options.keys.world=Мир +options.keys.world.chunkoverlay=Chunk Boundary Overlay +options.keys.world.moboverlay=Mob Spawn Overlay +options.keys.world.highlight_tips=Подсвечивать всплывающие подсказки +options.keys.world.dawn=Рассвет +options.keys.world.noon=Полдень +options.keys.world.dusk=Сумерки +options.keys.world.midnight=Полночь +options.keys.world.rain=Дождь +options.keys.world.heal=Исцеление +options.keys.world.creative=Игровой режим +options.keys.world.creative.tip=Круг игровых режимов + +options.inventory=Инвентарь +options.inventory.widgetsenabled.true=NEI включён +options.inventory.widgetsenabled.false=NEI выключен +options.inventory.cheatmode.0=Режим рецептов +options.inventory.cheatmode.1=Режим полезностей +options.inventory.cheatmode.2=Чит-режим +options.inventory.layoutstyle=Стиль расположения +options.inventory.profileRecipes=Profile Recipes +options.inventory.profileRecipes.true=True +options.inventory.profileRecipes.false=False +options.inventory.itemIDs=ID предметов +options.inventory.itemIDs.0=Спрятаны +options.inventory.itemIDs.1=Авто +options.inventory.itemIDs.2=Показаны +options.inventory.utilities=Полезности +options.inventory.utilities.tip=Выберете особенности для режима полезностей +options.inventory.utilities.time=Время +options.inventory.utilities.rain=Дождь +options.inventory.utilities.heal=Исцеление +options.inventory.utilities.delete=Удаление +options.inventory.utilities.magnet=Магнит +options.inventory.utilities.gamemode=Игровой режим +options.inventory.utilities.enchant=Зачарование +options.inventory.utilities.potion=Зелья +options.inventory.utilities.item=Предметы +options.inventory.gamemodes=Игровые режимы +options.inventory.gamemodes.tip=Выберете игровые режимы для переключения между ними +options.inventory.gamemodes.creative=Творческий +options.inventory.gamemodes.creative+=Творческий+ +options.inventory.gamemodes.adventure=Приключение + +options.command=Команды +options.command.creative=Игровой режим +options.command.creative.tip={0} = 1 для творческого, 0 для выживания, {1} = игрок +options.command.item=Item +options.command.item.tip={0} = player, {1} = itemID, {2} = quantity, {3} = itemDamage +options.command.time=Время +options.command.time.tip={0} = мировое время +options.command.rain=Дождь +options.command.rain.tip={0} = 1 для включения, 0 для выключения +options.command.heal=Исцелить +options.command.heal.tip={0} = игрок + +options.world.highlight_tips=Подсвечивать всплывающие подсказки +options.world.highlight_tips.hide=Спрятаны +options.world.highlight_tips.show=Показаны + +options.tools=Инструментарий +options.tools.dump=Дампы памяти +options.tools.dump.dump=Дамп +options.tools.dump.dumped=ID %s сдампленны в %s +options.tools.dump.mode.0=Используемые +options.tools.dump.mode.1=Свободные +options.tools.dump.mode.2=Все +options.tools.dump.item=Блока/Предмета +options.tools.dump.items=Блоков/Предметов +options.tools.dump.potion=Зелье +options.tools.dump.potions=Зелья +options.tools.dump.enchantment=Зачарование +options.tools.dump.enchantments=Зачарования +options.tools.dump.biome=Биом +options.tools.dump.biomes=Биомы +options.tools.dump.itempanel=Предметная панель +options.tools.dump.itempanel.dumped=Содержание предметной панели сдампленно в %s +options.tools.dump.itempanel.mode.0=CSV +options.tools.dump.itempanel.mode.1=NBT + +layoutstyle.minecraft=Minecraft +layoutstyle.oldtmi=Старый TMI + +recipe.page=Страница %s/%s +recipe.shaped=Форменный крафтинг +recipe.shapeless=Бесформенный крафтинг +recipe.furnace=Выплавка(обжарка) +recipe.fuel=Топливо +recipe.fuel.required=%s required +recipe.fuel.processed=%s processed +recipe.fuel.produced=%s produced +recipe.firework=Фейерверки +recipe.firework.tooltip0=Dyes add colours to the charges +recipe.firework.tooltip1=Recolouring adds fade colours to the charge +recipe.firework.tooltip2=Multiple charges can be spawned from one rocket +recipe.brewing=Зельеварение +recipe.profiler.crafting=Crafting Profiling +recipe.profiler.usage=Usage Profiling \ No newline at end of file diff --git a/resources/assets/nei/lang/tr_TR.lang b/resources/assets/nei/lang/tr_TR.lang new file mode 100644 index 00000000..3a3b9736 --- /dev/null +++ b/resources/assets/nei/lang/tr_TR.lang @@ -0,0 +1,159 @@ +load=Yükle +save=Kaydet + +enchant=Büyü +enchant.level=Level +enchant.locked=Kilitli +enchant.unlocked=Açık + +inventory.options=Ayarlar +inventory.prev=Geri +inventory.next=İleri +inventory.item_subsets=Item/Mod Listesi +inventory.delete.all=Mevcut envanterdeki tüm itemleri sil +inventory.delete.enable=Silme modunu aktif et +inventory.delete.1=Silme modunu aç +inventory.delete.0=Silme modunu kapat +inventory.rain.enable=yağmur aktif et +inventory.rain.1=Yağmur yağdır +inventory.rain.0=Yağmuru durdur +inventory.magnet.enable=Mıknatısı aktif et +inventory.magnet.1=Mıknatısı aç +inventory.magnet.0=Mıknatısı kapat +inventory.dawn.enable=Sabah-Öğle aktif et +inventory.dawn.set=Saati sabah(6:00) yap +inventory.noon.enable=Öğlen-akşam aktif et +inventory.noon.set=Saati öğlen(12:00) yap +inventory.dusk.enable=Akşam-Gece aktif et +inventory.dusk.set=Saati akşam(00:00) yap +inventory.midnight.enable=Gece-Sabah aktif et +inventory.midnight.set=Saati gece(00:00) yap +inventory.heal=Oyuncuya can ver +inventory.gamemode.0= Oyunu Hayatta kalma modu yap +inventory.gamemode.1= Oyunu Yaratıcı mod yap +inventory.gamemode.2= Oyunu Yaratıcı mod+ yap +inventory.gamemode.3= Oyunu Macera modu yap + +chat.gamemode.0=Hayatta kalma modunu oynuyorsunuz +chat.gamemode.1=Yaratıcı modu oynuyorsunuz +chat.gamemode.2=yaratıcı mod+ oynuyorsunuz +chat.gamemode.3=Macera modunu oynuyorsunuz + +options=NEI Ayarları +options.back=Geri +options.world=Dünya +options.global=Genel +options.global.tip.0=Ayarlar tüm dünyalar için kaydedilecek +options.global.tip.1=W tuşuna tıklayın:seçeneği aç:dünyada gör +options.wbutton.tip.0=Genel ayarları kullanıyorsunuz +options.wbutton.tip.1=Genel ayarların üstüne yazılıyor + +options.keys=Tuşlar +options.keys.gui=Envanter +options.keys.gui.recipe=Tarifler +options.keys.gui.usage=Kullanım yeri +options.keys.gui.enchant=Büylüler +options.keys.gui.potion=İksirler +options.keys.gui.back=Önceki tarif +options.keys.gui.prev=Önceki sayfa +options.keys.gui.next=Sonraki sayfa +options.keys.gui.hide=Nei görünürlük ayarı +options.keys.gui.search=Arama çubuğu odağı +options.keys.world=Dünya +options.keys.world.chunkoverlay=Chunk sınır yerleşimi +options.keys.world.moboverlay=Mob Spawn yerleşimi +options.keys.world.highlight_tips=Nesne ipucu +options.keys.world.dawn=Sabah +options.keys.world.noon=Öğlen +options.keys.world.dusk=Akşam +options.keys.world.midnight=Gece +options.keys.world.rain=Yağmur +options.keys.world.heal=Can +options.keys.world.creative=Oyun modu +options.keys.world.creative.tip=Cycle Gamemode + +options.inventory=Envarter +options.inventory.widgetsenabled.true=NEI Açık +options.inventory.widgetsenabled.false=NEI Kapalı +options.inventory.cheatmode.0=Tarif modu +options.inventory.cheatmode.1=Yardımcı mod +options.inventory.cheatmode.2=Hileli mod +options.inventory.layoutstyle=Tema +options.inventory.profileRecipes=Profil tarifleri +options.inventory.profileRecipes.true=Doğru +options.inventory.profileRecipes.false=Yanlış +options.inventory.itemIDs=Item IDleri +options.inventory.itemIDs.0=Gizli +options.inventory.itemIDs.1=Otomatik +options.inventory.itemIDs.2=Göster +options.inventory.utilities=Yardımcılar +options.inventory.utilities.tip=Yardımcıları seç +options.inventory.utilities.time=Saat +options.inventory.utilities.rain=Yağmur +options.inventory.utilities.heal=Can +options.inventory.utilities.delete=Sil +options.inventory.utilities.magnet=Mıknatıs +options.inventory.utilities.gamemode=Oyun modu +options.inventory.utilities.enchant=Büyüleme +options.inventory.utilities.potion=İksirler +options.inventory.utilities.item=Itemler +options.inventory.gamemodes=Oyun modu +options.inventory.gamemodes.tip=Select the gamemodes to cycle through +options.inventory.gamemodes.creative=Yaratıcı mod +options.inventory.gamemodes.creative+=Yaratıcı mod+ +options.inventory.gamemodes.adventure=Macera + +options.command=Komutlar +options.command.creative=Oyun modu +options.command.creative.tip={0} = 1 for creative, 0 for survival, {1} = player +options.command.item=Item +options.command.item.tip={0} = player, {1} = itemID, {2} = quantity, {3} = itemDamage +options.command.time=Zaman +options.command.time.tip={0} = worldtime +options.command.rain=Yağmur +options.command.rain.tip={0} = 1 for on, 0 for off +options.command.heal=Can +options.command.heal.tip={0} = player + +options.world.highlight_tips=Nesne ipucu +options.world.highlight_tips.hide=Kapalı +options.world.highlight_tips.show=Açık + +options.tools=Araçlar +options.tools.dump=Verileri Silme +options.tools.dump.dump=Sil +options.tools.dump.dumped=%s IDs silindi %s +options.tools.dump.mode.0=Kullanıldı +options.tools.dump.mode.1=Boş +options.tools.dump.mode.2=Hepsi +options.tools.dump.item=Blok/Item +options.tools.dump.items=Bloklar/Itemlar +options.tools.dump.potion=İksir +options.tools.dump.potions=İksirler +options.tools.dump.enchantment=Büyü +options.tools.dump.enchantments=Büyüler +options.tools.dump.biome=Biyom +options.tools.dump.biomes=Biyomlar +options.tools.dump.itempanel=Item Paneli +options.tools.dump.itempanel.dumped=Item panelinden %s kadar silindi +options.tools.dump.itempanel.mode.0=CSV +options.tools.dump.itempanel.mode.1=NBT + +layoutstyle.minecraft=Minecraft +layoutstyle.oldtmi=Eski TMI + +recipe.page=Sayfa %s/%s +recipe.shaped=Üretim +recipe.shapeless=Üretim +recipe.furnace=Pişirme +recipe.fuel=Yakıt +recipe.fuel.required=%s gerekli +recipe.fuel.processed=%s işlenmiş +recipe.fuel.produced=%s üretilmiş +recipe.firework=Havaifişek +recipe.firework.tooltip0=Renk ekle veya değiştir +recipe.firework.tooltip1=Rengi kaldır veya değiştir +recipe.firework.tooltip2=Fazlaca roket yap veya ışınla +recipe.brewing=İksir Standı +recipe.profiler.crafting=Üretme Profili +recipe.profiler.usage=Kullanma Profili \ No newline at end of file diff --git a/resources/assets/nei/lang/zh_CN.lang b/resources/assets/nei/lang/zh_CN.lang new file mode 100644 index 00000000..c2db1e0c --- /dev/null +++ b/resources/assets/nei/lang/zh_CN.lang @@ -0,0 +1,140 @@ +#zh_CN + +load=载入 +save=保存 + +enchant=附魔 +enchant.level=等级 +enchant.locked=锁定 +enchant.unlocked=解锁 + +inventory.options=选项 +inventory.prev=上一页 +inventory.next=下一页 +inventory.item_subsets=物品分类 +inventory.delete.all=清空物品栏 +inventory.delete.enable=激活删除设置 +inventory.delete.1=开启删除模式 +inventory.delete.0=关闭删除模式 +inventory.rain.enable=激活天气设置 +inventory.rain.1=开启雨雪 +inventory.rain.0=关闭雨雪 +inventory.magnet.enable=激活磁力设置 +inventory.magnet.1=开启磁力模式 +inventory.magnet.0=关闭磁力模式 +inventory.dawn.enable=激活破晓时间设置 +inventory.dawn.set=设置时间为破晓 +inventory.noon.enable=激活正午时间设置 +inventory.noon.set=设置时间为正午 +inventory.dusk.enable=激活黄昏时间设置 +inventory.dusk.set=设置时间为黄昏 +inventory.midnight.enable=激活午夜时间设置 +inventory.midnight.set=设置时间为午夜 +inventory.heal=治愈玩家并回满饱食度 +inventory.gamemode.0=设置为生存模式 +inventory.gamemode.1=设置为创造模式 +inventory.gamemode.2=设置为创造模式+ +inventory.gamemode.3=设置为冒险模式 + +chat.gamemode.0=生存模式开启 +chat.gamemode.1=创造模式开启 +chat.gamemode.2=创造模式+开启 +chat.gamemode.3=冒险模式开启 + +options=NEI选项 +options.back=后退 +options.world=世界 +options.global=全局 +options.global.tip.0=选项适用于所有世界 +options.global.tip.1=点击选项左侧W按钮可覆写该世界设置 +options.wbutton.tip.0=使用全局设置 +options.wbutton.tip.1=覆写全局设置 + +options.keys=按键绑定 +options.keys.gui=物品栏 +options.keys.gui.recipe=查看合成 +options.keys.gui.usage=查看用途 +options.keys.gui.enchant=附魔 +options.keys.gui.potion=药水 +options.keys.gui.back=上一个合成表 +options.keys.gui.prev=上一页 +options.keys.gui.next=下一页 +options.keys.gui.hide=隐藏/显示NEI界面 +options.keys.world=世界 +options.keys.world.chunkoverlay=区块(chunks)边界显示 +options.keys.world.moboverlay=刷怪区域显示 +options.keys.world.highlight_tips=显示选中方块名称 +options.keys.world.dawn=破晓 +options.keys.world.noon=正午 +options.keys.world.dusk=黄昏 +options.keys.world.midnight=午夜 +options.keys.world.rain=降雨 +options.keys.world.heal=治疗 +options.keys.world.creative=游戏模式 +options.keys.world.creative.tip=循环游戏模式 + +options.inventory=物品栏 +options.inventory.widgetsenabled.true=激活NEI +options.inventory.widgetsenabled.false=禁用NEI +options.inventory.cheatmode.0=合成模式 +options.inventory.cheatmode.1=实用模式 +options.inventory.cheatmode.2=作弊模式 +options.inventory.layoutstyle=界面风格 +options.inventory.profileRecipes=合成查阅性能分析 +options.inventory.profileRecipes.true=开启 +options.inventory.profileRecipes.false=关闭 +options.inventory.itemIDs=物品ID +options.inventory.itemIDs.0=隐藏 +options.inventory.itemIDs.1=自动 +options.inventory.itemIDs.2=显示 +options.inventory.utilities=按钮 +options.inventory.utilities.tip=选择实用模式的功能 +options.inventory.utilities.time=时间 +options.inventory.utilities.rain=降雨 +options.inventory.utilities.heal=治疗 +options.inventory.utilities.delete=删除 +options.inventory.utilities.magnet=磁铁 +options.inventory.utilities.gamemode=游戏模式 +options.inventory.utilities.enchant=附魔 +options.inventory.utilities.potion=酿造 +options.inventory.utilities.item=保存物品 +options.inventory.gamemodes=游戏模式 +options.inventory.gamemodes.tip=选择游戏模式循环调整时的选项 +options.inventory.gamemodes.creative=创造模式 +options.inventory.gamemodes.creative+=创造模式+ +options.inventory.gamemodes.adventure=冒险模式 + +options.command=指令 +options.command.creative=游戏模式 +options.command.creative.tip={0}=1为创造模式,0为生存模式,{1}=玩家 +options.command.item=物品 +options.command.item.tip={0}=玩家,{1}=物品ID,{2}=数量,{3}=物品耐久度 +options.command.time=时间 +options.command.time.tip={0}=世界时间 +options.command.rain=雨雪 +options.command.rain.tip={0}=1为开启雨雪,0为关闭雨雪 +options.command.heal=治疗 +options.command.heal.tip={0}=玩家 + +options.world.highlight_tips=高亮显示 +options.world.highlight_tips.hide=隐藏 +options.world.highlight_tips.show=显示 + +layoutstyle.minecraft=Minecraft风格 +layoutstyle.oldtmi=老式TMI风格 + +recipe.page=页数 %s/%s +recipe.shaped=有序合成 +recipe.shapeless=无序合成 +recipe.furnace=烧制 +recipe.fuel=燃料 +recipe.fuel.required=需要%s个 +recipe.fuel.processed=可烧制%s个 +recipe.fuel.produced=可制作%s个 +recipe.firework=烟花 +recipe.firework.tooltip0=添加多种染料可增加烟火之星的颜色 +recipe.firework.tooltip1=合成好的烟火之星和染料合成,可以添加色彩淡入效果 +recipe.firework.tooltip2=多个烟火之星可被用于合成单个烟花火箭 +recipe.brewing=酿造 +recipe.profiler.crafting=合成查询性能分析 +recipe.profiler.usage=用途查询性能分析 \ No newline at end of file diff --git a/resources/assets/nei/lang/zh_TW.lang b/resources/assets/nei/lang/zh_TW.lang new file mode 100644 index 00000000..5fa20b4b --- /dev/null +++ b/resources/assets/nei/lang/zh_TW.lang @@ -0,0 +1,159 @@ +load=讀取 +save=保存 + +enchant=附魔 +enchant.level=等級 +enchant.locked=開啟限制 +enchant.unlocked=解除限制 + +inventory.options=設定 +inventory.prev=上一頁 +inventory.next=下一頁 +inventory.item_subsets=物品分類表 +inventory.delete.all=刪除當前物品欄內所有物品 +inventory.delete.enable=開啟刪除模式設定 +inventory.delete.1=開啟刪除模式 +inventory.delete.0=關閉刪除模式 +inventory.rain.enable=開啟雨雪設定 +inventory.rain.1=開啟雨雪 +inventory.rain.0=關閉雨雪 +inventory.magnet.enable=開啟磁力模式設定 +inventory.magnet.1=開啟磁力模式 +inventory.magnet.0=關閉磁力模式 +inventory.dawn.enable=開啟黎明時間設定 +inventory.dawn.set=設定時間為黎明 +inventory.noon.enable=開啟中午時間設定 +inventory.noon.set=設定時間為中午 +inventory.dusk.enable=開啟傍晚時間設定 +inventory.dusk.set=設定時間為傍晚 +inventory.midnight.enable=開啟午夜時間設定 +inventory.midnight.set=設定時間為午夜 +inventory.heal=治療玩家並回滿飽食度 +inventory.gamemode.0=設定遊戲模式為生存 +inventory.gamemode.1=設定遊戲模式為創造 +inventory.gamemode.2=設定遊戲模式為創造+ +inventory.gamemode.3=設定遊戲模式為冒險 + +chat.gamemode.0=生存模式開啟 +chat.gamemode.1=創造模式開啟 +chat.gamemode.2=創造模式+開啟 +chat.gamemode.3=冒險模式開啟 + +options=NEI 選項 +options.back=後退 +options.world=世界 +options.global=全局 +options.global.tip.0=選項適用於所有世界 +options.global.tip.1=點擊旁邊的W可重寫該世界設置 +options.wbutton.tip.0=使用全局設定 +options.wbutton.tip.1=覆蓋全局設定 + +options.keys=按鍵設定 +options.keys.gui=物品欄 +options.keys.gui.recipe=合成表 +options.keys.gui.usage=用途 +options.keys.gui.enchant=附魔 +options.keys.gui.potion=藥水 +options.keys.gui.back=上一個合成表 +options.keys.gui.prev=上一頁 +options.keys.gui.next=下一頁 +options.keys.gui.hide=隱藏/顯示NEI界面 +options.keys.gui.search=焦點搜索欄 +options.keys.world=世界 +options.keys.world.chunkoverlay=區塊邊界顯示 +options.keys.world.moboverlay=怪物生成區域顯示 +options.keys.world.highlight_tips=高亮工具提示 +options.keys.world.dawn=黎明 +options.keys.world.noon=中午 +options.keys.world.dusk=傍晚 +options.keys.world.midnight=午夜 +options.keys.world.rain=雨雪 +options.keys.world.heal=治療 +options.keys.world.creative=遊戲模式 +options.keys.world.creative.tip=循環遊戲模式 + +options.inventory=NEI設定 +options.inventory.widgetsenabled.true=NEI開啟 +options.inventory.widgetsenabled.false=NEI關閉 +options.inventory.cheatmode.0=合成模式 +options.inventory.cheatmode.1=實用模式 +options.inventory.cheatmode.2=作弊模式 +options.inventory.layoutstyle=界面風格 +options.inventory.profileRecipes=合成查詢 +options.inventory.profileRecipes.true=開啟 +options.inventory.profileRecipes.false=關閉 +options.inventory.itemIDs=物品ID +options.inventory.itemIDs.0=隱藏 +options.inventory.itemIDs.1=自動 +options.inventory.itemIDs.2=顯示 +options.inventory.utilities=按鈕 +options.inventory.utilities.tip=選擇實用模式的功能 +options.inventory.utilities.time=時間 +options.inventory.utilities.rain=雨雪 +options.inventory.utilities.heal=治療 +options.inventory.utilities.delete=刪除 +options.inventory.utilities.magnet=磁力 +options.inventory.utilities.gamemode=遊戲模式 +options.inventory.utilities.enchant=附魔 +options.inventory.utilities.potion=釀造 +options.inventory.utilities.item=保存物品 +options.inventory.gamemodes=遊戲模式 +options.inventory.gamemodes.tip=選擇遊戲模式循環調整時的選項 +options.inventory.gamemodes.creative=創造模式 +options.inventory.gamemodes.creative+=創造模式+ +options.inventory.gamemodes.adventure=冒險模式 + +options.command=指令 +options.command.creative=遊戲模式 +options.command.creative.tip={0}=1為創造模式,0為生存模式,{1}=玩家 +options.command.give=物品 +options.command.give.tip={0}=玩家,{1}=物品ID,{2}=數量,{3}=物品耐久度 +options.command.time=時間 +options.command.time.tip={0}=世界時間 +options.command.rain=雨雪 +options.command.rain.tip={0}=1為開啟雨雪,0為關閉雨雪 +options.command.heal=治療 +options.command.heal.tip={0}=玩家 + +options.world.highlight_tips=高亮工具提示 +options.world.highlight_tips.hide=隱藏 +options.world.highlight_tips.show=顯示 + +options.tools=工具 +options.tools.dump=數據轉儲 +options.tools.dump.dump=轉儲 +options.tools.dump.dumped=%s ID 轉儲到 %s +options.tools.dump.mode.0=使用 +options.tools.dump.mode.1=自由 +options.tools.dump.mode.2=所有 +options.tools.dump.item=方塊/物品 +options.tools.dump.items=方塊/物品 +options.tools.dump.potion=藥水 +options.tools.dump.potions=藥水 +options.tools.dump.enchantment=附魔 +options.tools.dump.enchantments=附魔 +options.tools.dump.biome=生物群落 +options.tools.dump.biomes=生物群落 +options.tools.dump.itempanel=物品面板 +options.tools.dump.itempanel.dumped=物品面板內容轉儲為 %s +options.tools.dump.itempanel.mode.0=CSV +options.tools.dump.itempanel.mode.1=NBT + +layoutstyle.minecraft=Minecraft風格 +layoutstyle.oldtmi=舊TMI風格 + +recipe.page=頁數 %s/%s +recipe.shaped=成型工藝 +recipe.shapeless=成型染色工藝 +recipe.furnace=提煉 +recipe.fuel=燃料 +recipe.fuel.required=需要%s個 +recipe.fuel.processed=可燒製%s個 +recipe.fuel.produced=可製作%s個 +recipe.firework=煙花 +recipe.firework.tooltip0=多種染料可以增加火藥球的顏色 +recipe.firework.tooltip1=通過用完成的火藥球和染料合成,可以添加色彩淡出的效果 +recipe.firework.tooltip2=多種火藥球可以放進一個煙花火箭 +recipe.brewing=釀造 +recipe.profiler.crafting=合成查詢性能分析 +recipe.profiler.usage=用途查詢性能分析 \ No newline at end of file diff --git a/resources/assets/nei/textures/gui/inv.png b/resources/assets/nei/textures/gui/inv.png new file mode 100644 index 0000000000000000000000000000000000000000..4b032b907a5dfbda9d721ccb861171f9bda4bfee GIT binary patch literal 3926 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 T$W!zK=gUynOb4Uw{NTR;xDTxV literal 0 HcmV?d00001 diff --git a/resources/assets/nei/textures/gui/potion.png b/resources/assets/nei/textures/gui/potion.png new file mode 100644 index 0000000000000000000000000000000000000000..ee450e292f049e461ec5c0014053d3fab0c49ff1 GIT binary patch literal 3990 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⪻0%1bJsIzzWSn(X8a+)O+`w z`IvOR7q>AA16s&GpfErkqG+h+|E>UvHKbk`Ix0)*81S0{9iTz;pBch;AOKJ(v|#vi W!4F3{XcF}a17m#)y;_7z%s&B7>XG6A literal 0 HcmV?d00001 diff --git a/resources/assets/nei/textures/nei_sprites.png b/resources/assets/nei/textures/nei_sprites.png new file mode 100644 index 0000000000000000000000000000000000000000..1fec7bd197faebdf1c0f13020175a15ea009760a GIT binary patch literal 28790 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 gNVu@VN)ye handleTooltip(int mx, int my, List tooltip) + { + if(!contains(mx, my)) + return tooltip; + + String tip = getButtonTip(); + if(tip != null) + tooltip.add(tip); + return tooltip; + } + + public String getButtonTip() + { + return null; + } + + public String getRenderLabel() + { + return label; + } + + public String label; + public Image icon; + + /** + * 0x4 = state flag, as opposed to 1 click + * 0 = normal + * 1 = on + * 2 = disabled + */ + public int state; +} diff --git a/src/codechicken/nei/ButtonCycled.java b/src/codechicken/nei/ButtonCycled.java new file mode 100644 index 00000000..eb1c5736 --- /dev/null +++ b/src/codechicken/nei/ButtonCycled.java @@ -0,0 +1,13 @@ +package codechicken.nei; + +public abstract class ButtonCycled extends Button +{ + @Override + public Image getRenderIcon() + { + return icons[index]; + } + + public int index; + public Image[] icons; +} diff --git a/src/codechicken/nei/ClientHandler.java b/src/codechicken/nei/ClientHandler.java new file mode 100644 index 00000000..0db0f3e8 --- /dev/null +++ b/src/codechicken/nei/ClientHandler.java @@ -0,0 +1,231 @@ +package codechicken.nei; + +import codechicken.core.ClientUtils; +import codechicken.lib.packet.PacketCustom; +import codechicken.nei.api.API; +import codechicken.nei.api.ItemInfo; +import cpw.mods.fml.client.CustomModLoadingErrorDisplayException; +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.TickEvent; +import cpw.mods.fml.common.gameevent.TickEvent.Phase; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.*; +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.world.World; +import net.minecraftforge.client.event.RenderWorldLastEvent; +import net.minecraftforge.common.MinecraftForge; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class ClientHandler +{ + private static ClientHandler instance; + + private ArrayList SMPmagneticItems = new ArrayList(); + private World lastworld; + private GuiScreen lastGui; + + public void addSMPMagneticItem(int i, World world) { + WorldClient cworld = (WorldClient) world; + Entity e = cworld.getEntityByID(i); + if (e == null || !(e instanceof EntityItem)) { + return; + } + SMPmagneticItems.add((EntityItem) e); + } + + private void updateMagnetMode(World world, EntityPlayerSP player) { + if (!NEIClientConfig.getMagnetMode()) return; + + float distancexz = 16; + float distancey = 8; + double maxspeedxz = 0.5; + double maxspeedy = 0.5; + double speedxz = 0.05; + double speedy = 0.07; + + List items; + if (world.isRemote) { + items = SMPmagneticItems; + } else { + items = world.getEntitiesWithinAABB(EntityItem.class, player.boundingBox.expand(distancexz, distancey, distancexz)); + } + for (Iterator iterator = items.iterator(); iterator.hasNext(); ) { + EntityItem item = iterator.next(); + + if (item.delayBeforeCanPickup > 0) continue; + if (item.isDead && world.isRemote) iterator.remove(); + + if (!NEIClientUtils.canItemFitInInventory(player, item.getEntityItem())) continue; + + double dx = player.posX - item.posX; + double dy = player.posY + player.getEyeHeight() - item.posY; + double dz = player.posZ - item.posZ; + double absxz = Math.sqrt(dx * dx + dz * dz); + double absy = Math.abs(dy); + if (absxz > distancexz) { + continue; + } + + if (absxz > 1) { + dx /= absxz; + dz /= absxz; + } + + if (absy > 1) { + dy /= absy; + } + + double vx = item.motionX + speedxz * dx; + double vy = item.motionY + speedy * dy; + double vz = item.motionZ + speedxz * dz; + + double absvxz = Math.sqrt(vx * vx + vz * vz); + double absvy = Math.abs(vy); + + double rationspeedxz = absvxz / maxspeedxz; + if (rationspeedxz > 1) { + vx /= rationspeedxz; + vz /= rationspeedxz; + } + + double rationspeedy = absvy / maxspeedy; + if (rationspeedy > 1) { + vy /= rationspeedy; + } + + if (absvxz < 0.2 && absxz < 0.2 && world.isRemote) { + item.setDead(); + } + + item.setVelocity(vx, vy, vz); + } + } + + public static void load() { + try { + TMIUninstaller.deleteTMIUninstaller(); + if (TMIUninstaller.TMIInstalled()) { + TMIUninstaller.runTMIUninstaller(); + NEIClientUtils.mc().shutdownMinecraftApplet(); + } + } catch (Exception e) { + System.err.println("Error with TMI Uninstaller"); + e.printStackTrace(); + } + + instance = new ClientHandler(); + + PacketCustom.assignHandler(NEICPH.channel, new NEICPH()); + FMLCommonHandler.instance().bus().register(instance); + MinecraftForge.EVENT_BUS.register(instance); + + API.registerHighlightHandler(new DefaultHighlightHandler(), ItemInfo.Layout.HEADER); + HUDRenderer.load(); + WorldOverlayRenderer.load(); + } + + @SubscribeEvent + public void tickEvent(TickEvent.ClientTickEvent event) { + if(event.phase == Phase.END) + return; + + Minecraft mc = Minecraft.getMinecraft(); + if(mc.theWorld != null) { + loadWorld(mc.theWorld, false); + + if (!NEIClientConfig.isEnabled()) + return; + + KeyManager.tickKeyStates(); + + NEIController.updateUnlimitedItems(mc.thePlayer.inventory); + if (mc.currentScreen == null) + NEIController.processCreativeCycling(mc.thePlayer.inventory); + + updateMagnetMode(mc.theWorld, mc.thePlayer); + } + + GuiScreen gui = mc.currentScreen; + if (gui != lastGui) { + if (gui instanceof GuiMainMenu) + lastworld = null; + else if (gui instanceof GuiSelectWorld) + NEIClientConfig.reloadSaves(); + } + lastGui = gui; + } + + @SubscribeEvent + public void tickEvent(TickEvent.RenderTickEvent event) { + if(event.phase == Phase.END && NEIClientConfig.isEnabled()) + HUDRenderer.renderOverlay(); + } + + + @SubscribeEvent + public void renderLastEvent(RenderWorldLastEvent event) { + if (NEIClientConfig.isEnabled()) + WorldOverlayRenderer.render(event.partialTicks); + } + + + public void loadWorld(World world, boolean remote) { + if (world != lastworld) { + SMPmagneticItems.clear(); + WorldOverlayRenderer.reset(); + + if (!remote) { + NEIClientConfig.setHasSMPCounterPart(false); + NEIClientConfig.setInternalEnabled(false); + + if (!Minecraft.getMinecraft().isSingleplayer())//wait for server to initiate + NEIClientConfig.loadWorld("remote/" + ClientUtils.getServerIP().replace(':', '~')); + } + + lastworld = world; + } + } + + public static ClientHandler instance() { + return instance; + } + + public static RuntimeException throwCME(final String message) { + final GuiScreen errorGui = new GuiErrorScreen(null, null) + { + @Override + public void handleMouseInput() {} + + @Override + public void handleKeyboardInput() {} + + @Override + public void drawScreen(int par1, int par2, float par3) { + drawDefaultBackground(); + String[] s_msg = message.split("\n"); + for (int i = 0; i < s_msg.length; ++i) + drawCenteredString(fontRendererObj, s_msg[i], width / 2, height / 3 + 12 * i, 0xFFFFFFFF); + } + }; + + @SuppressWarnings("serial") + CustomModLoadingErrorDisplayException e = new CustomModLoadingErrorDisplayException() + { + @Override + public void initGui(GuiErrorScreen errorScreen, FontRenderer fontRenderer) { + Minecraft.getMinecraft().displayGuiScreen(errorGui); + } + + @Override + public void drawScreen(GuiErrorScreen errorScreen, FontRenderer fontRenderer, int mouseRelX, int mouseRelY, float tickTime) {} + }; + throw e; + } +} diff --git a/src/codechicken/nei/ContainerCreativeInv.java b/src/codechicken/nei/ContainerCreativeInv.java new file mode 100644 index 00000000..e528d742 --- /dev/null +++ b/src/codechicken/nei/ContainerCreativeInv.java @@ -0,0 +1,112 @@ +package codechicken.nei; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; + +public class ContainerCreativeInv extends Container +{ + private class SlotArmor extends Slot + { + int armorType; + + public SlotArmor(IInventory inv, int x, int y, int slot, int armor) { + super(inv, x, y, slot); + armorType = armor; + } + + @Override + public int getSlotStackLimit() { + return 1; + } + + @Override + public boolean isItemValid(ItemStack stack) { + return stack != null && stack.getItem().isValidArmor(stack, armorType, player); + } + + @Override + @SideOnly(Side.CLIENT) + public IIcon getBackgroundIconIndex() { + return ItemArmor.func_94602_b(armorType); + } + } + + private class SlotBlockArmor extends SlotArmor + { + public SlotBlockArmor(IInventory inv, int x, int y, int slot, int armor) { + super(inv, x, y, slot, armor); + } + + @Override + public boolean isItemValid(ItemStack stack) { + return super.isItemValid(stack) || stack != null;// && Block.getBlockFromItem(stack.getItem()) != Blocks.air; + } + } + + public EntityPlayer player; + + public ContainerCreativeInv(EntityPlayer player, ExtendedCreativeInv extraInv) { + this.player = player; + InventoryPlayer invPlayer = player.inventory; + for (int row = 0; row < 6; row++) + for (int col = 0; col < 9; col++) + addSlotToContainer(new Slot(extraInv, col + row * 9, 8 + col * 18, 5 + row * 18)); + + for (int row = 0; row < 3; ++row) + for (int col = 0; col < 9; ++col) + addSlotToContainer(new Slot(invPlayer, col + row * 9 + 9, 8 + col * 18, 118 + row * 18)); + + for (int col = 0; col < 9; ++col) + addSlotToContainer(new Slot(invPlayer, col, 8 + col * 18, 176)); + + addSlotToContainer(new SlotBlockArmor(invPlayer, invPlayer.getSizeInventory() - 1, -15, 23, 0)); + for (int armorType = 1; armorType < 4; armorType++) + addSlotToContainer(new SlotArmor(invPlayer, invPlayer.getSizeInventory() - 1 - armorType, -15, 23 + armorType * 18, armorType)); + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer par1EntityPlayer, int slotIndex) { + ItemStack transferredStack = null; + Slot slot = (Slot) inventorySlots.get(slotIndex); + + if (slot != null && slot.getHasStack()) { + ItemStack stack = slot.getStack(); + transferredStack = stack.copy(); + + if (stack.getItem() instanceof ItemArmor) { + ItemArmor armor = (ItemArmor) stack.getItem(); + if (!getSlot(90 + armor.armorType).getHasStack()) { + getSlot(90 + armor.armorType).putStack(transferredStack); + slot.putStack(null); + return transferredStack; + } + } + + if (slotIndex < 54) { + if (!this.mergeItemStack(stack, 54, 90, true)) + return null; + } else if (!this.mergeItemStack(stack, 0, 54, false)) { + return null; + } + + if (stack.stackSize == 0) + slot.putStack(null); + else + slot.onSlotChanged(); + } + + return transferredStack; + } + + public boolean canInteractWith(EntityPlayer var1) { + return true; + } +} diff --git a/src/codechicken/nei/ContainerEnchantmentModifier.java b/src/codechicken/nei/ContainerEnchantmentModifier.java new file mode 100644 index 00000000..1e169628 --- /dev/null +++ b/src/codechicken/nei/ContainerEnchantmentModifier.java @@ -0,0 +1,322 @@ +package codechicken.nei; + +import codechicken.lib.render.CCRenderState; +import net.minecraft.client.gui.Gui; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ContainerEnchantment; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.world.World; +import org.lwjgl.opengl.GL11; + +import java.util.ArrayList; + +/** + * This is crap code, don't ever do this. + */ +public class ContainerEnchantmentModifier extends ContainerEnchantment +{ + public static class EnchantmentHash + { + public EnchantmentHash(Enchantment e, int i, int l) { + enchantment = e; + state = i; + level = l; + } + + Enchantment enchantment; + int state; + int level; + } + + public ArrayList slotEnchantment = new ArrayList(); + int level = 5; + + public int scrollclicky = -1; + public float scrollpercent; + public int scrollmousey; + public float percentscrolled; + public int relx = 60; + public int rely = 14; + public int height = 57; + public int cwidth = 101; + public int slotheight = 19; + public GuiEnchantmentModifier parentscreen; + + public ContainerEnchantmentModifier(InventoryPlayer inventoryplayer, World world, int i, int j, int k) { + super(inventoryplayer, world, i, j, k); + } + + public int getNumSlots() { + return slotEnchantment.size(); + } + + public int getScrollBarHeight() { + int sbarh = (int) ((height / (float) getContentHeight()) * height); + if (sbarh > height) { + return height; + } else if (sbarh < height / 15) { + return height / 15; + } else { + return sbarh; + } + } + + public int getScrollBarWidth() { + return 7; + } + + public int getContentHeight() { + return slotheight * getNumSlots(); + } + + public int getScrolledSlots() { + int slots = getNumSlots(); + int shownslots = height / slotheight; + return (int) (percentscrolled * (slots - shownslots) + 0.5F); + } + + private int getClickedSlot(int mousey) { + return ((mousey - rely) / slotheight) + getScrolledSlots(); + } + + public void calculatePercentScrolled() { + int barempty = height - getScrollBarHeight(); + if (scrollclicky >= 0) { + int scrolldiff = scrollmousey - scrollclicky; + percentscrolled = scrolldiff / (float) barempty + scrollpercent; + } + if (percentscrolled < 0) { + percentscrolled = 0; + } + if (percentscrolled > 1) { + percentscrolled = 1; + } + int sbary = rely + (int) (barempty * percentscrolled + 0.5); + percentscrolled = (sbary - rely) / (float) barempty; + } + + public boolean clickScrollBar(int mousex, int mousey, int button) { + mousex -= parentscreen.guiLeft; + mousey -= parentscreen.guiTop; + + int barempty = height - getScrollBarHeight(); + int sbary = rely + (int) (barempty * percentscrolled + 0.5); + + if (button == 0 && + getScrollBarHeight() < height && //the scroll bar can move (not full length) + mousex >= relx + cwidth && mousex < relx + cwidth + getScrollBarWidth() && + mousey >= rely && mousey < rely + height)//in the scroll pane + { + if (mousey < sbary) { + percentscrolled = (mousey - rely) / (float) barempty; + calculatePercentScrolled(); + } else if (mousey > sbary + getScrollBarHeight()) { + percentscrolled = (mousey - rely - getScrollBarHeight() + 1) / (float) barempty; + calculatePercentScrolled(); + } else { + scrollclicky = mousey; + scrollpercent = percentscrolled; + scrollmousey = mousey; + } + return true; + } + return false; + } + + public void mouseUp(int mousex, int mousey, int button) { + if (scrollclicky >= 0 && button == 0)//we were scrolling and we released mouse + { + scrollclicky = -1; + } + } + + public boolean clickButton(int mousex, int mousey, int button) { + mousex -= parentscreen.guiLeft; + mousey -= parentscreen.guiTop; + if (mousex >= relx && mousex < relx + cwidth && + mousey >= rely && mousey <= rely + height)//in the box + { + int slot = getClickedSlot(mousey); + if (slot >= getNumSlots()) return false; + toggleSlotEnchantment(slot); + return true; + } + return false; + } + + private void toggleSlotEnchantment(int slot) { + EnchantmentHash e = slotEnchantment.get(slot); + if (e.state == 2) { + NEICPH.sendModifyEnchantment(e.enchantment.effectId, 0, false); + e.state = 0; + } else if (e.state == 1) { + return; + } else { + NEICPH.sendModifyEnchantment(e.enchantment.effectId, level, true); + e.state = 2; + } + updateEnchantmentOptions(GuiEnchantmentModifier.validateEnchantments()); + } + + public boolean addEnchantment(int e, int level) { + if (e < Enchantment.enchantmentsList.length && Enchantment.enchantmentsList[e] != null) { + ((Slot) inventorySlots.get(0)).getStack().addEnchantment(Enchantment.enchantmentsList[e], level); + return true; + } + return false; + } + + public void removeEnchantment(int e) { + ItemStack stack = ((Slot) inventorySlots.get(0)).getStack(); + NBTTagList nbttaglist = stack.getEnchantmentTagList(); + if (nbttaglist != null) { + for (int i = 0; i < nbttaglist.tagCount(); i++) { + int ID = nbttaglist.getCompoundTagAt(i).getShort("id"); + if (ID == e) { + nbttaglist.removeTag(i); + if (nbttaglist.tagCount() == 0) + stack.getTagCompound().removeTag("ench"); + if (stack.getTagCompound().hasNoTags()) + stack.setTagCompound(null); + return; + } + } + } + } + + public boolean canInteractWith(EntityPlayer entityplayer) { + return true; + } + + public void onCraftMatrixChanged(IInventory iinventory) { + if (parentscreen != null) + updateEnchantmentOptions(GuiEnchantmentModifier.validateEnchantments()); + } + + public void updateEnchantmentOptions(boolean validate) { + int numoptions = slotEnchantment.size(); + slotEnchantment.clear(); + + ItemStack toolstack = getSlot(0).getStack(); + if (toolstack == null) { + percentscrolled = 0; + return; + } + + Item item = toolstack.getItem(); + int enchantablity = item.getItemEnchantability(); + if (enchantablity == 0 && validate) { + percentscrolled = 0; + return; + } + + for (Enchantment e : Enchantment.enchantmentsList) { + if (e == null || e.type == null || (!e.type.canEnchantItem(item) && validate)) { + continue; + } + int state = 0; + int level = -1; + if (NEIServerUtils.stackHasEnchantment(toolstack, e.effectId)) { + state = 2; + level = NEIServerUtils.getEnchantmentLevel(toolstack, e.effectId); + } else if (NEIServerUtils.doesEnchantmentConflict(NEIServerUtils.getEnchantments(toolstack), e) && validate) { + state = 1; + } + slotEnchantment.add(new EnchantmentHash(e, state, level)); + } + if (numoptions != slotEnchantment.size()) { + percentscrolled = 0; + } + } + + public void drawSlots(GuiEnchantmentModifier gui) { + for (int slot = 0; slot < 3; slot++) { + int shade = 0; + String text = ""; + + int containerslot = slot + getScrolledSlots(); + if (containerslot + 1 > slotEnchantment.size()) { + shade = 1; + } else { + EnchantmentHash e = slotEnchantment.get(containerslot); + shade = e.state; + text = e.enchantment.getTranslatedName(e.level == -1 ? level : e.level); + if (gui.mc.fontRenderer.getStringWidth(text) > 95 && text.contains("Projectile")) { + text = text.replace("Projectile", "Proj"); + } + if (gui.mc.fontRenderer.getStringWidth(text) > 95 && text.contains("Protection")) { + text = text.replace("Protection", "Protect"); + } + if (gui.mc.fontRenderer.getStringWidth(text) > 95 && text.contains("Bane of")) { + text = text.replace("Bane of ", ""); + } + } + + CCRenderState.changeTexture("textures/gui/container/enchanting_table.png"); + GL11.glColor3f(1, 1, 1); + if (hasScrollBar()) { + gui.drawTexturedModalRect(relx, rely + slot * slotheight, 0, gui.ySize + slotheight * shade, cwidth - 30, slotheight); + gui.drawTexturedModalRect(relx + cwidth - 30, rely + slot * slotheight, cwidth - 23, gui.ySize + slotheight * shade, 30, slotheight); + } else { + gui.drawTexturedModalRect(relx, rely + slot * slotheight, 0, gui.ySize + slotheight * shade, cwidth + 7, slotheight); + } + + gui.getFontRenderer().drawString(text, relx + 4, rely + slot * slotheight + 5, textColourFromState(shade)); + } + } + + private boolean hasScrollBar() { + return getNumSlots() > 3; + } + + public void drawScrollBar(GuiEnchantmentModifier gui) { + if (!hasScrollBar()) return; + + int sbary = rely + (int) ((height - getScrollBarHeight()) * percentscrolled + 0.5); + int sbarx = relx + cwidth; + + Gui.drawRect(sbarx, rely, sbarx + getScrollBarWidth(), rely + height, 0xFF202020);//background + Gui.drawRect(sbarx, sbary, sbarx + getScrollBarWidth(), sbary + getScrollBarHeight(), 0xFF8B8B8B);//corners + Gui.drawRect(sbarx, sbary, sbarx + getScrollBarWidth() - 1, sbary + getScrollBarHeight() - 1, 0xFFF0F0F0);//topleft up + Gui.drawRect(sbarx + 1, sbary + 1, sbarx + getScrollBarWidth() - 1, sbary + getScrollBarHeight() - 1, 0xFF555555);//bottom right down + Gui.drawRect(sbarx + 1, sbary + 1, sbarx + getScrollBarWidth() - 2, sbary + getScrollBarHeight() - 2, 0xFFC6C6C6);//scrollbar + } + + private int textColourFromState(int shade) { + switch(shade) { + case 0: return 0x685e4a; + case 1: return 0x407f10; + default: return 0xffff80; + } + } + + public void onUpdate(int mousex, int mousey) { + processScrollMouse(mousey); + } + + public void processScrollMouse(int mousey) { + mousey -= parentscreen.guiTop; + + if (scrollclicky >= 0) { + int scrolldiff = mousey - scrollclicky; + int barupallowed = (int) ((height - getScrollBarHeight()) * scrollpercent + 0.5); + int bardownallowed = (height - getScrollBarHeight()) - barupallowed; + + if (-scrolldiff > barupallowed) { + scrollmousey = scrollclicky - barupallowed; + } else if (scrolldiff > bardownallowed) { + scrollmousey = scrollclicky + bardownallowed; + } else { + scrollmousey = mousey; + } + + calculatePercentScrolled(); + } + } +} diff --git a/src/codechicken/nei/ContainerPotionCreator.java b/src/codechicken/nei/ContainerPotionCreator.java new file mode 100644 index 00000000..e6b7c9a5 --- /dev/null +++ b/src/codechicken/nei/ContainerPotionCreator.java @@ -0,0 +1,162 @@ +package codechicken.nei; + +import codechicken.core.inventory.ContainerExtended; +import codechicken.core.inventory.SlotHandleClicks; +import codechicken.lib.inventory.InventoryNBT; +import codechicken.lib.inventory.InventoryUtils; +import codechicken.lib.packet.PacketCustom; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.InventoryBasic; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemPotion; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.potion.PotionEffect; + +public class ContainerPotionCreator extends ContainerExtended +{ + public class SlotPotion extends Slot + { + public SlotPotion(IInventory inv, int slotIndex, int x, int y) { + super(inv, slotIndex, x, y); + } + + @Override + public boolean isItemValid(ItemStack stack) { + return stack.getItem() instanceof ItemPotion; + } + + @Override + public void onSlotChanged() { + super.onSlotChanged(); + if (getHasStack()) { + ItemStack stack = getStack(); + if (!stack.hasTagCompound()) + stack.setTagCompound(new NBTTagCompound()); + if (!stack.getTagCompound().hasKey("CustomPotionEffects")) + stack.getTagCompound().setTag("CustomPotionEffects", new NBTTagList()); + } + } + } + + public class SlotPotionStore extends SlotHandleClicks + { + public SlotPotionStore(IInventory inv, int slotIndex, int x, int y) { + super(inv, slotIndex, x, y); + } + + @Override + public ItemStack slotClick(ContainerExtended container, EntityPlayer player, int button, int modifier) { + ItemStack held = player.inventory.getItemStack(); + if (button == 0 && modifier == 1) { + NEIClientUtils.cheatItem(getStack(), button, -1); + } else if (button == 1) { + putStack(null); + } else if (held != null) { + if (isItemValid(held)) { + putStack(InventoryUtils.copyStack(held, 1)); + player.inventory.setItemStack(null); + } + } else if (getHasStack()) + player.inventory.setItemStack(getStack()); + + return null; + } + + @Override + public boolean isItemValid(ItemStack stack) { + return stack.getItem() instanceof ItemPotion; + } + } + + public static class InventoryPotionStore extends InventoryNBT + { + public InventoryPotionStore() { + super(9, NEIClientConfig.global.nbt.getCompoundTag("potionStore")); + } + + @Override + public void markDirty() { + super.markDirty(); + NEIClientConfig.global.nbt.setTag("potionStore", tag); + NEIClientConfig.global.saveNBT(); + } + } + + InventoryPlayer playerInv; + InventoryBasic potionInv; + IInventory potionStoreInv; + + public ContainerPotionCreator(InventoryPlayer inventoryPlayer, IInventory potionStoreInv) { + playerInv = inventoryPlayer; + potionInv = new InventoryBasic("Potion", true, 1); + this.potionStoreInv = potionStoreInv; + + addSlotToContainer(new SlotPotion(potionInv, 0, 25, 102)); + for (int i = 0; i < 9; i++) + addSlotToContainer(new SlotPotionStore(potionStoreInv, i, 8 + i * 18, 14)); + bindPlayerInventory(inventoryPlayer, 8, 125); + } + + @Override + public boolean doMergeStackAreas(int slotIndex, ItemStack stack) { + if (slotIndex < 10) + return mergeItemStack(stack, 10, 46, true); + return mergeItemStack(stack, 0, 1, false); + } + + @Override + public boolean canInteractWith(EntityPlayer entityplayer) { + return true; + } + + @Override + public void onContainerClosed(EntityPlayer player) { + super.onContainerClosed(player); + + if (!player.worldObj.isRemote) + InventoryUtils.dropOnClose(player, potionInv); + } + + @SuppressWarnings("unchecked") + @Override + public void handleInputPacket(PacketCustom packet) { + ItemStack potion = potionInv.getStackInSlot(0); + if (potion == null) + return; + + boolean add = packet.readBoolean(); + int effectID = packet.readUByte(); + + NBTTagList effects = potion.getTagCompound().getTagList("CustomPotionEffects", 10); + NBTTagList newEffects = new NBTTagList(); + for(int i = 0; i < effects.tagCount(); i++) { + NBTTagCompound tag = effects.getCompoundTagAt(i); + PotionEffect e = PotionEffect.readCustomPotionEffectFromNBT(tag); + if(e.getPotionID() != effectID) + newEffects.appendTag(tag); + } + if(add) + newEffects.appendTag(new PotionEffect(effectID, packet.readInt(), packet.readUByte()).writeCustomPotionEffectToNBT(new NBTTagCompound())); + potion.getTagCompound().setTag("CustomPotionEffects", newEffects); + } + + public void setPotionEffect(int effectID, int duration, int amplifier) { + PacketCustom packet = NEICPH.createContainerPacket(); + packet.writeBoolean(true); + packet.writeByte(effectID); + packet.writeInt(duration); + packet.writeByte(amplifier); + packet.sendToServer(); + } + + public void removePotionEffect(int effectID) { + PacketCustom packet = NEICPH.createContainerPacket(); + packet.writeBoolean(false); + packet.writeByte(effectID); + packet.sendToServer(); + } +} \ No newline at end of file diff --git a/src/codechicken/nei/DefaultHighlightHandler.java b/src/codechicken/nei/DefaultHighlightHandler.java new file mode 100644 index 00000000..785e4c65 --- /dev/null +++ b/src/codechicken/nei/DefaultHighlightHandler.java @@ -0,0 +1,50 @@ +package codechicken.nei; + +import codechicken.nei.api.IHighlightHandler; +import codechicken.nei.api.ItemInfo; +import codechicken.nei.guihook.GuiContainerManager; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.world.World; + +import java.util.List; + +public class DefaultHighlightHandler implements IHighlightHandler +{ + @Override + public List handleTextData(ItemStack stack, World world, EntityPlayer player, MovingObjectPosition mop, List currenttip, ItemInfo.Layout layout) + { + String name = null; + try + { + String s = GuiContainerManager.itemDisplayNameShort(stack); + if(s != null && !s.endsWith("Unnamed")) + name = s; + + if(name != null) + currenttip.add(name); + } + catch(Exception e) + { + } + + if(stack.getItem() == Items.redstone) + { + int md = world.getBlockMetadata(mop.blockX, mop.blockY, mop.blockZ); + String s = ""+md; + if(s.length() < 2) + s=" "+s; + currenttip.set(currenttip.size()-1, name+" "+s); + } + + return currenttip; + } + + @Override + public ItemStack identifyHighlight(World world, EntityPlayer player, MovingObjectPosition mop) + { + return null; + } +} diff --git a/src/codechicken/nei/ExtendedCreativeInv.java b/src/codechicken/nei/ExtendedCreativeInv.java new file mode 100644 index 00000000..1a992b27 --- /dev/null +++ b/src/codechicken/nei/ExtendedCreativeInv.java @@ -0,0 +1,109 @@ +package codechicken.nei; + +import cpw.mods.fml.relauncher.Side; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; + +public class ExtendedCreativeInv implements IInventory +{ + PlayerSave playerSave; + Side side; + + public ExtendedCreativeInv(PlayerSave playerSave, Side side) { + this.playerSave = playerSave; + this.side = side; + } + + @Override + public int getSizeInventory() { + return 54; + } + + @Override + public ItemStack getStackInSlot(int slot) { + if (side.isClient()) + return NEIClientConfig.creativeInv[slot]; + return playerSave.creativeInv[slot]; + } + + @Override + public ItemStack decrStackSize(int slot, int size) { + ItemStack item = getStackInSlot(slot); + + if (item != null) { + if (item.stackSize <= size) { + ItemStack itemstack = item; + setInventorySlotContents(slot, null); + markDirty(); + return itemstack; + } + ItemStack itemstack1 = item.splitStack(size); + if (item.stackSize == 0) { + setInventorySlotContents(slot, null); + } + markDirty(); + return itemstack1; + } + return null; + } + + @Override + public ItemStack getStackInSlotOnClosing(int slot) { + synchronized (this) { + ItemStack stack = getStackInSlot(slot); + setInventorySlotContents(slot, null); + return stack; + } + } + + @Override + public void setInventorySlotContents(int slot, ItemStack stack) { + if (side.isClient()) + NEIClientConfig.creativeInv[slot] = stack; + else + playerSave.creativeInv[slot] = stack; + + markDirty(); + } + + @Override + public String getInventoryName() { + return "Extended Creative"; + } + + @Override + public int getInventoryStackLimit() { + return 64; + } + + @Override + public void markDirty() { + if (side.isServer()) + playerSave.setCreativeDirty(); + } + + @Override + public boolean isUseableByPlayer(EntityPlayer var1) { + return true; + } + + @Override + public void openInventory() { + } + + @Override + public void closeInventory() { + } + + @Override + public boolean isItemValidForSlot(int slot, ItemStack stack) { + return true; + } + + @Override + public boolean hasCustomInventoryName() { + return true; + } + +} diff --git a/src/codechicken/nei/FastTransferManager.java b/src/codechicken/nei/FastTransferManager.java new file mode 100644 index 00000000..bbd14437 --- /dev/null +++ b/src/codechicken/nei/FastTransferManager.java @@ -0,0 +1,338 @@ +package codechicken.nei; + +import codechicken.nei.guihook.GuiContainerManager; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +import java.util.*; + +import static codechicken.nei.NEIServerUtils.*; + +public class FastTransferManager +{ + /** + * Based on the general assumption that we want to fill top to bottom, left to right + */ + public static class SlotPositionComparator implements Comparator + { + Container container; + + public SlotPositionComparator(Container c) { + container = c; + } + + @Override + public int compare(Integer arg0, Integer arg1) { + Slot slot1 = container.getSlot(arg0); + Slot slot2 = container.getSlot(arg1); + + if (slot2.yDisplayPosition != slot1.yDisplayPosition) + return slot1.yDisplayPosition - slot2.yDisplayPosition; + return slot1.xDisplayPosition - slot2.xDisplayPosition; + } + } + + public LinkedList> slotZones = new LinkedList>(); + public HashMap slotZoneMap = new HashMap(); + + private void generateSlotMap(Container container, ItemStack stack) { + stack = stack.copy(); + stack.stackSize = 1; + + for (int slotNo = 0; slotNo < container.inventorySlots.size(); slotNo++) { + if (slotZoneMap.containsKey(slotNo) || !container.getSlot(slotNo).isItemValid(stack)) + continue; + + HashSet connectedSlots = new HashSet(); + findConnectedSlots(container, slotNo, connectedSlots); + + LinkedList zoneSlots = new LinkedList(connectedSlots); + Collections.sort(zoneSlots, new SlotPositionComparator(container)); + slotZones.add(zoneSlots); + + for (int i : zoneSlots) { + slotZoneMap.put(i, slotZones.size() - 1); + } + } + } + + private void findConnectedSlots(Container container, int slotNo, HashSet connectedSlots) { + connectedSlots.add(slotNo); + Slot slot = container.getSlot(slotNo); + final int threshold = 18; + + for (int i = 0; i < container.inventorySlots.size(); i++) { + if (connectedSlots.contains(i)) + continue; + + Slot slot1 = container.getSlot(i); + if (Math.abs(slot.xDisplayPosition - slot1.xDisplayPosition) <= threshold && Math.abs(slot.yDisplayPosition - slot1.yDisplayPosition) <= threshold) { + findConnectedSlots(container, i, connectedSlots); + } + } + } + + public static int findSlotWithItem(Container container, ItemStack teststack) { + for (int slotNo = 0; slotNo < container.inventorySlots.size(); slotNo++) { + ItemStack stack = container.getSlot(slotNo).getStack(); + if (stack != null && areStacksSameType(stack, teststack)) + return slotNo; + } + return -1; + } + + public static void clearSlots(Container container) { + for (int slotNo = 0; slotNo < container.inventorySlots.size(); slotNo++) + ((Slot) container.inventorySlots.get(slotNo)).putStack(null); + } + + public void performMassTransfer(GuiContainer window, int fromSlot, int toSlot, ItemStack heldStack) { + generateSlotMap(window.inventorySlots, heldStack); + + Integer fromZone = slotZoneMap.get(fromSlot); + Integer toZone = slotZoneMap.get(toSlot); + + if (fromZone == null || toZone == null || fromZone == toZone) + return; + + if (NEIClientUtils.getHeldItem() != null && !areStacksSameType(heldStack, NEIClientUtils.getHeldItem())) + return; + + if (!fillZoneWithHeldItem(window, toZone)) + return; + + for (int transferFrom : slotZones.get(fromZone)) { + ItemStack transferStack = window.inventorySlots.getSlot(transferFrom).getStack(); + + if (!areStacksSameType(heldStack, transferStack)) + continue; + + clickSlot(window, transferFrom); + if (!fillZoneWithHeldItem(window, toZone)) { + clickSlot(window, transferFrom); + return; + } + } + } + + /** + * @return The slot that one item from the source slot will end up in apon shift clicking, -1 if none. + */ + public int findShiftClickDestinationSlot(Container container, int fromSlot) { + LinkedList save = saveContainer(container); + + Slot slot = container.getSlot(fromSlot); + ItemStack stack = slot.getStack(); + if (stack == null) + return -1; + + stack.stackSize = 1; + slot.putStack(stack.copy()); + + LinkedList compareBefore = saveContainer(container); + container.slotClick(fromSlot, 0, 1, NEIClientUtils.mc().thePlayer); + LinkedList compareAfter = saveContainer(container); + + try { + //if(compareAfter.get(fromSlot) != null)//transfer failed + //return -1; + + for (int i = 0; i < compareBefore.size(); i++) { + if (i == fromSlot) + continue; + + ItemStack before = compareBefore.get(i); + ItemStack after = compareAfter.get(i); + + if (!areStacksIdentical(before, after) && after != null) { + if ((before == null && areStacksSameType(stack, after)) ||//transfered into this empty slot + (areStacksSameType(stack, after) && + after.stackSize - + before.stackSize > 0))//it added to this stack + return i; + } + } + + return -1; + } finally { + restoreContainer(container, save); + } + } + + public LinkedList saveContainer(Container container) { + LinkedList stacks = new LinkedList(); + for (int i = 0; i < container.inventorySlots.size(); i++) + stacks.add(copyStack(container.getSlot(i).getStack())); + + return stacks; + } + + public void restoreContainer(Container container, LinkedList items) { + for (int i = 0; i < container.inventorySlots.size(); i++) { + container.getSlot(i).putStack(items.get(i)); + } + + container.slotClick(-999, 0, 0, NEIClientUtils.mc().thePlayer); + } + + public void transferItem(GuiContainer window, int fromSlot) { + int toSlot = findShiftClickDestinationSlot(window.inventorySlots, fromSlot); + if (toSlot == -1) + return; + + Slot from = window.inventorySlots.getSlot(fromSlot); + + if (from.isItemValid(from.getStack())) + moveOneItem(window, fromSlot, toSlot); + else//slots that you can't put stuff in + moveOutputSet(window, fromSlot, toSlot); + } + + public void moveOutputSet(GuiContainer window, int fromSlot, int toSlot) { + if (NEIClientUtils.getHeldItem() != null) + return; + + clickSlot(window, fromSlot);//pickup fromSlot + if (NEIClientUtils.getHeldItem() == null)//maybe this container does auto transfers. No need to pick up the final item + return; + clickSlot(window, toSlot);//place one in toSlot + } + + public void moveOneItem(GuiContainer window, int fromSlot, int toSlot) { + clickSlot(window, fromSlot);//pickup fromSlot + clickSlot(window, toSlot, 1);//place one in toSlot + clickSlot(window, fromSlot);//place down in fromSlot + } + + public void retrieveItem(GuiContainer window, int toSlot) { + Slot slot = window.inventorySlots.getSlot(toSlot); + ItemStack slotStack = slot.getStack(); + if (slotStack == null || + slotStack.stackSize == slot.getSlotStackLimit() || + slotStack.stackSize == slotStack.getMaxStackSize()) + return; + + generateSlotMap(window.inventorySlots, slotStack); + + Integer destZone = slotZoneMap.get(toSlot); + if (destZone == null)//slots that don't accept + return; + + int firstZoneSlot = findShiftClickDestinationSlot(window.inventorySlots, toSlot); + int firstZone = -1; + if (firstZoneSlot != -1) { + Integer integer = slotZoneMap.get(firstZoneSlot); + if (integer != null) { + firstZone = integer; + if (retrieveItemFromZone(window, firstZone, toSlot)) + return; + } + } + + for (int zone = 0; zone < slotZones.size(); zone++) { + if (zone == destZone || zone == firstZone) + continue; + + if (retrieveItemFromZone(window, zone, toSlot)) + return; + } + + retrieveItemFromZone(window, destZone, toSlot); + } + + private boolean retrieveItemFromZone(GuiContainer window, int zone, int toSlot) { + ItemStack stack = window.inventorySlots.getSlot(toSlot).getStack(); + for (int i : slotZones.get(zone)) { + if (i == toSlot) + continue; + + Slot slot = window.inventorySlots.getSlot(i); + ItemStack stack1 = slot.getStack(); + + if (areStacksSameType(stack, stack1) && + stack1.stackSize != slot.getSlotStackLimit() && //get from full stacks on second pass + stack1.stackSize != stack1.getMaxStackSize()) { + moveOneItem(window, i, toSlot); + return true; + } + } + + for (int i : slotZones.get(zone)) { + if (i == toSlot) + continue; + + Slot slot = window.inventorySlots.getSlot(i); + ItemStack stack1 = slot.getStack(); + + if (areStacksSameType(stack, stack1)) { + moveOneItem(window, i, toSlot); + return true; + } + } + return false; + } + + public static void clickSlot(GuiContainer window, int slotIndex) { + clickSlot(window, slotIndex, 0); + } + + public static void clickSlot(GuiContainer window, int slotIndex, int button) { + clickSlot(window, slotIndex, button, 0); + } + + public static void clickSlot(GuiContainer window, int slotIndex, int button, int modifier) { + GuiContainerManager.getManager(window).handleSlotClick(slotIndex, button, modifier); + } + + private boolean fillZoneWithHeldItem(GuiContainer window, int zoneIndex) { + for (int transferTo : slotZones.get(zoneIndex)) { + ItemStack held = NEIClientUtils.getHeldItem(); + + if (held == null) + break; + + ItemStack inToSlot = window.inventorySlots.getSlot(transferTo).getStack(); + + if (!areStacksSameType(inToSlot, held)) + continue; + + clickSlot(window, transferTo); + } + + for (int transferTo : slotZones.get(zoneIndex))//repeat on empty slots + { + ItemStack held = NEIClientUtils.getHeldItem(); + + if (held == null) + break; + + ItemStack inToSlot = window.inventorySlots.getSlot(transferTo).getStack(); + + if (inToSlot != null) + continue; + + clickSlot(window, transferTo); + } + + return NEIClientUtils.getHeldItem() == null; + } + + public void throwAll(GuiContainer window, int pickedUpFromSlot) { + ItemStack held = NEIClientUtils.getHeldItem(); + if (held == null) + return; + + clickSlot(window, -999); + + generateSlotMap(window.inventorySlots, held); + for (int slotIndex : slotZones.get(slotZoneMap.get(pickedUpFromSlot))) { + Slot slot = window.inventorySlots.getSlot(slotIndex); + if (areStacksSameType(held, slot.getStack())) { + clickSlot(window, slotIndex); + clickSlot(window, -999); + } + } + } +} diff --git a/src/codechicken/nei/GuiEnchantmentModifier.java b/src/codechicken/nei/GuiEnchantmentModifier.java new file mode 100644 index 00000000..d2d5740a --- /dev/null +++ b/src/codechicken/nei/GuiEnchantmentModifier.java @@ -0,0 +1,99 @@ +package codechicken.nei; + +import codechicken.lib.render.CCRenderState; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.world.World; +import org.lwjgl.opengl.GL11; + +import static codechicken.nei.NEIClientUtils.translate; + +public class GuiEnchantmentModifier extends GuiContainer +{ + ContainerEnchantmentModifier container; + + public GuiEnchantmentModifier(InventoryPlayer inventoryplayer, World world, int i, int j, int k) { + super(new ContainerEnchantmentModifier(inventoryplayer, world, i, j, k)); + container = (ContainerEnchantmentModifier) inventorySlots; + container.parentscreen = this; + } + + @Override + protected void drawGuiContainerForegroundLayer(int par1, int par2) { + fontRendererObj.drawString(translate("enchant"), 12, 6, 0x404040); + fontRendererObj.drawString(translate("enchant.level"), 19, 20, 0x404040); + } + + protected void drawGuiContainerBackgroundLayer(float f, int i, int j) { + GL11.glColor4f(1, 1, 1, 1); + CCRenderState.changeTexture("textures/gui/container/enchanting_table.png"); + GL11.glTranslatef(guiLeft, guiTop, 0); + drawTexturedModalRect(0, 0, 0, 0, xSize, ySize); + + container.onUpdate(i, j); + container.drawSlots(this); + container.drawScrollBar(this); + + String levelstring = "" + container.level; + fontRendererObj.drawString(levelstring, 33 - fontRendererObj.getStringWidth(levelstring) / 2, 34, 0xFF606060); + + GL11.glTranslatef(-guiLeft, -guiTop, 0); + } + + public void initGui() { + super.initGui(); + + buttonList.add(new GuiNEIButton(0, width / 2 - 78, height / 2 - 52, 12, 12, "<")); + buttonList.add(new GuiNEIButton(1, width / 2 - 44, height / 2 - 52, 12, 12, ">")); + buttonList.add(new GuiNEIButton(2, width / 2 - 80, height / 2 - 15, 50, 12, lockDisplayString())); + } + + private String lockDisplayString() { + return validateEnchantments() ? translate("enchant.locked") : translate("enchant.unlocked"); + } + + public static boolean validateEnchantments() { + return NEIClientConfig.world.nbt.getBoolean("validateenchantments"); + } + + public static void toggleEnchantmentValidation() { + NEIClientConfig.world.nbt.setBoolean("validateenchantments", !validateEnchantments()); + NEIClientConfig.world.saveNBT(); + } + + protected void actionPerformed(GuiButton guibutton) { + if (guibutton.id == 0) { + changeLevel(-1); + } else if (guibutton.id == 1) { + changeLevel(1); + } else if (guibutton.id == 2) { + toggleEnchantmentValidation(); + container.updateEnchantmentOptions(validateEnchantments()); + guibutton.displayString = lockDisplayString(); + } + } + + private void changeLevel(int i) { + container.level += i; + ((GuiButton) buttonList.get(0)).enabled = container.level != 1; + ((GuiButton) buttonList.get(1)).enabled = container.level != 10; + } + + protected void mouseClicked(int i, int j, int k) { + if (container.clickButton(i, j, k)) return; + if (container.clickScrollBar(i, j, k)) return; + + super.mouseClicked(i, j, k); + } + + protected void mouseMovedOrUp(int i, int j, int k) { + container.mouseUp(i, j, k); + super.mouseMovedOrUp(i, j, k); + } + + public FontRenderer getFontRenderer() { + return fontRendererObj; + } +} diff --git a/src/codechicken/nei/GuiExtendedCreativeInv.java b/src/codechicken/nei/GuiExtendedCreativeInv.java new file mode 100644 index 00000000..5c658602 --- /dev/null +++ b/src/codechicken/nei/GuiExtendedCreativeInv.java @@ -0,0 +1,56 @@ +package codechicken.nei; + +import codechicken.lib.render.CCRenderState; +import codechicken.nei.api.INEIGuiHandler; +import codechicken.nei.api.TaggedInventoryArea; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Container; +import net.minecraft.item.ItemStack; +import org.lwjgl.opengl.GL11; + +import java.util.Arrays; +import java.util.List; + +public class GuiExtendedCreativeInv extends GuiContainer implements INEIGuiHandler +{ + public GuiExtendedCreativeInv(Container par1Container) { + super(par1Container); + ySize = 198; + } + + @Override + protected void drawGuiContainerBackgroundLayer(float var1, int var2, int var3) { + GL11.glColor4f(1, 1, 1, 1); + CCRenderState.changeTexture("nei:textures/gui/inv.png"); + + int x = guiLeft; + int y = guiTop - 4; + + drawTexturedModalRect(x - 23, y, 0, 0, 199, 204); + } + + @Override + public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) { + return currentVisibility; + } + + @Override + public Iterable getItemSpawnSlots(GuiContainer gui, ItemStack item) { + return NEIServerUtils.getRange(0, 54); + } + + @Override + public List getInventoryAreas(GuiContainer gui) { + return Arrays.asList(new TaggedInventoryArea("ExtendedCreativeInv", 0, 54, inventorySlots)); + } + + @Override + public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) { + return false; + } + + @Override + public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) { + return false; + } +} diff --git a/src/codechicken/nei/GuiNEIButton.java b/src/codechicken/nei/GuiNEIButton.java new file mode 100644 index 00000000..c63fa357 --- /dev/null +++ b/src/codechicken/nei/GuiNEIButton.java @@ -0,0 +1,41 @@ +package codechicken.nei; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.opengl.GL11; + +public class GuiNEIButton extends GuiButton +{ + protected static ResourceLocation guiTex = new ResourceLocation("textures/gui/widgets.png"); + + public GuiNEIButton(int i, int j, int k, int l, int i1, String s) + { + super(i, j, k, l, i1, s); + } + + public void drawButton(Minecraft minecraft, int i, int j) + { + if(!visible) + return; + + FontRenderer fontrenderer = minecraft.fontRenderer; + minecraft.renderEngine.bindTexture(guiTex); + GL11.glColor4f(1, 1, 1, 1); + boolean flag = i >= xPosition && j >= yPosition && i < xPosition + width && j < yPosition + height; + int k = getHoverState(flag); + drawTexturedModalRect(xPosition, yPosition, 0, 46 + k * 20, width / 2, height / 2);//top left + drawTexturedModalRect(xPosition + width / 2, yPosition, 200 - width / 2, 46 + k * 20, width / 2, height / 2);//top right + drawTexturedModalRect(xPosition, yPosition + height / 2, 0, 46 + k * 20 + 20 - height / 2, width / 2, height / 2);//bottom left + drawTexturedModalRect(xPosition + width / 2, yPosition + height / 2, 200 - width / 2, 46 + k * 20 + 20 - height / 2, width / 2, height / 2);//bottom right + mouseDragged(minecraft, i, j); + + if(!enabled) + drawCenteredString(fontrenderer, displayString, xPosition + width / 2, yPosition + (height - 8) / 2, 0xffa0a0a0); + else if(flag) + drawCenteredString(fontrenderer, displayString, xPosition + width / 2, yPosition + (height - 8) / 2, 0xffffa0); + else + drawCenteredString(fontrenderer, displayString, xPosition + width / 2, yPosition + (height - 8) / 2, 0xe0e0e0); + } +} diff --git a/src/codechicken/nei/GuiPotionCreator.java b/src/codechicken/nei/GuiPotionCreator.java new file mode 100644 index 00000000..4191b119 --- /dev/null +++ b/src/codechicken/nei/GuiPotionCreator.java @@ -0,0 +1,330 @@ +package codechicken.nei; + +import codechicken.core.gui.GuiCCButton; +import codechicken.core.gui.GuiCCTextField; +import codechicken.core.gui.GuiScrollSlot; +import codechicken.core.inventory.GuiContainerWidget; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.render.FontUtils; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.StatCollector; +import org.lwjgl.opengl.GL11; + +import java.util.ArrayList; + +public class GuiPotionCreator extends GuiContainerWidget +{ + public class GuiSlotPotionEffects extends GuiScrollSlot + { + public int selectedslot = -1; + public boolean enabled = true; + private ArrayList validPotions = new ArrayList(); + + public GuiSlotPotionEffects(int x, int y) { + super(x, y, 108, 76); + for (Potion p : Potion.potionTypes) + if (p != null) + validPotions.add(p); + setSmoothScroll(false); + setContentSize(x, y, height); + } + + @Override + public int getSlotHeight() { + return 19; + } + + @Override + public void drawSlotBox(float frame) { + drawRect(x, y, x + width, y + height, 0xFF000000); + } + + @Override + public void drawOverlay(float frame) { + } + + @Override + public boolean drawLineGuide() { + return false; + } + + @Override + public int getScrollBarWidth() { + return 7; + } + + @Override + protected void drawSlot(int slot, int x, int y, int mx, int my, boolean selected, float frame) { + GL11.glColor4f(1, 1, 1, 1); + Potion potion = validPotions.get(slot); + PotionEffect effect = getEffect(potion.id); + boolean blank = effect == null; + if (effect == null) + effect = new PotionEffect(potion.id, 1200, 0); + int shade = selectedslot == slot ? 2 : blank ? 1 : 0; + + CCRenderState.changeTexture("textures/gui/container/enchanting_table.png"); + drawTexturedModalRect(x, y, 0, 166 + getSlotHeight() * shade, width - 30, getSlotHeight()); + drawTexturedModalRect(x + width - 30, y, width - 23, 166 + getSlotHeight() * shade, 30, getSlotHeight()); + + if (potion.hasStatusIcon()) { + CCRenderState.changeTexture("textures/gui/container/inventory.png"); + int icon = potion.getStatusIconIndex(); + drawTexturedModalRect(x + 1, y + 1, 0 + icon % 8 * 18, 198 + icon / 8 * 18, 18, 18); + } + + String name = StatCollector.translateToLocal(potion.getName()); + String amp = effect.getAmplifier() > 0 ? " " + translateAmplifier(effect.getAmplifier()) : ""; + int textColour = shade == 0 ? 0x685e4a : shade == 1 ? 0x407f10 : 0xffff80; + if (fontRenderer.getStringWidth(name + amp) < width - 20) { + fontRenderer.drawString(name + amp, x + 20, y + 1, textColour); + } else { + fontRenderer.drawString(name, x + 20, y + 1, textColour); + FontUtils.drawRightString(amp, x + width - 10, y + 10, textColour); + } + + String duration = Potion.getDurationString(effect); + textColour = shade == 0 ? 0xA0A0A0 : shade == 1 ? 0x808080 : 0xCCCCCC; + fontRenderer.drawStringWithShadow(duration, x + 20, y + 10, textColour); + } + + private PotionEffect getEffect(int id) { + ItemStack potion = container.potionInv.getStackInSlot(0); + if (potion != null && potion.hasTagCompound() && potion.getTagCompound().hasKey("CustomPotionEffects")) { + NBTTagList potionTagList = potion.getTagCompound().getTagList("CustomPotionEffects", 10); + for (int i = 0; i < potionTagList.tagCount(); i++) { + PotionEffect effect = PotionEffect.readCustomPotionEffectFromNBT(potionTagList.getCompoundTagAt(i)); + if (effect.getPotionID() == id) + return effect; + } + } + return null; + } + + @Override + protected int getNumSlots() { + return validPotions.size(); + } + + @Override + protected boolean isSlotSelected(int slot) { + return slot == selectedslot; + } + + @Override + protected void slotClicked(int slot, int button, int mx, int my, boolean doubleclick) { + if (!enabled) + return; + + if (button == 0) { + select(slot); + applyEffect(); + } else if (button == 1) { + deselect(); + container.removePotionEffect(validPotions.get(slot).id); + } + } + + public void selectNext() { + if (selectedslot >= 0 && selectedslot + 1 < getNumSlots()) { + select(selectedslot + 1); + showSlot(selectedslot); + } + } + + public void selectPrev() { + if (selectedslot > 0) { + select(selectedslot - 1); + showSlot(selectedslot); + } + } + + public void setEnabled(boolean b) { + if (b == enabled) + return; + enabled = b; + if (!enabled) + deselect(); + } + + private void select(int slot) { + selectedslot = slot; + durationField.setEnabled(true); + PotionEffect effect = getEffect(validPotions.get(slot).id); + if (effect == null) + effect = new PotionEffect(validPotions.get(slot).id, 1200, 0); + durationField.setDurationTicks(effect.getDuration()); + amplifier = effect.getAmplifier(); + validateInputButtons(); + } + + private void deselect() { + selectedslot = -1; + durationField.setEnabled(false); + ampDown.setEnabled(false); + ampUp.setEnabled(false); + } + + public int selectedPotion() { + return validPotions.get(selectedslot).id; + } + } + + public class GuiDurationField extends GuiCCTextField + { + private String baseValue; + + public GuiDurationField(int x, int y, int width, int height) { + super(x, y, width, height, "100"); + setMaxStringLength(4); + setAllowedCharacters("0123456789"); + baseValue = getText(); + } + + public void setDurationTicks(int i) { + i /= 20; + String minutes = Integer.toString(i / 60); + String seconds = Integer.toString(i % 60); + if (seconds.length() == 1) + seconds = '0' + seconds; + setText(minutes + seconds); + } + + @Override + public void setEnabled(boolean b) { + super.setEnabled(b); + if (!isEnabled()) + setText("100"); + } + + @Override + public void onFocusChanged() { + if (isFocused()) + baseValue = getText(); + else { + if (!validateValue()) + setText(baseValue); + else + applyEffect(); + } + } + + @Override + public void onTextChanged(String oldText) { + validateInputButtons(); + } + + private boolean validateValue() { + try { + int i = Integer.parseInt(getText()); + return i > 1 && getDurationTicks() < Short.MAX_VALUE && i / 100 <= 60 && i % 100 < 60; + } catch (NumberFormatException nfe) { + return false; + } + } + + @Override + public void drawText() { + String s = getText(); + String seconds = s.substring(Math.max(0, s.length() - 2), Math.max(0, s.length() - 2) + Math.min(s.length(), 2)); + String minutes = s.length() < 3 ? "" : s.substring(0, s.length() - 2); + int ty = y + height / 2 - 4; + int tcolour = getTextColour(); + FontUtils.drawCenteredString(":", x + width / 2 + 1, ty, tcolour); + FontUtils.drawRightString(seconds, x + width - 3, ty, tcolour); + FontUtils.drawRightString(minutes, x + width / 2 - 1, ty, tcolour); + } + + @Override + public int getTextColour() { + return isFocused() ? 0xCCCCCC : 0x909090; + } + + public int getDurationTicks() { + int i = Integer.parseInt(getText()); + return ((i / 100) * 60 + (i % 100)) * 20; + } + } + + int amplifier = 0; + GuiCCButton ampDown; + GuiCCButton ampUp; + GuiSlotPotionEffects slotPotionEffects; + GuiDurationField durationField; + ContainerPotionCreator container; + + public GuiPotionCreator(InventoryPlayer inventoryplayer) { + super(new ContainerPotionCreator(inventoryplayer, new ContainerPotionCreator.InventoryPotionStore()), 176, 208); + container = (ContainerPotionCreator) inventorySlots; + } + + public void applyEffect() { + if (slotPotionEffects.selectedslot >= 0) + container.setPotionEffect(slotPotionEffects.selectedPotion(), durationField.getDurationTicks(), amplifier); + } + + public String translateAmplifier(int amplifier) { + switch (amplifier) { + case 0: + return "I"; + case 1: + return "II"; + case 2: + return "III"; + case 3: + return "IV"; + case 4: + return "V"; + } + return Integer.toString(amplifier); + } + + public void validateInputButtons() { + ampDown.setEnabled(amplifier > 0); + ampUp.setEnabled(amplifier < 3); + } + + @Override + protected void drawGuiContainerForegroundLayer(int par1, int par2) { + //fontRenderer.drawString("Potion", 12, 6, 0x404040); + } + + @Override + public void drawBackground() { + CCRenderState.changeTexture("nei:textures/gui/potion.png"); + drawTexturedModalRect(0, 0, 0, 0, xSize, ySize); + + FontUtils.drawCenteredString("Favourite Potions", xSize / 2, 4, 0x404040); + fontRendererObj.drawString("Duration", 12, 40, 0x404040); + fontRendererObj.drawString("Level", 19, 73, 0x404040); + FontUtils.drawCenteredString(translateAmplifier(amplifier), 33, 86, 0xFF606060); + } + + @Override + public void addWidgets() { + add(ampDown = new GuiCCButton(10, 84, 12, 12, "<").setActionCommand("ampDown")); + add(ampUp = new GuiCCButton(44, 84, 12, 12, ">").setActionCommand("ampUp")); + add(durationField = new GuiDurationField(15, 53, 35, 12)); + add(slotPotionEffects = new GuiSlotPotionEffects(60, 38)); + } + + @Override + public void updateScreen() { + super.updateScreen(); + slotPotionEffects.setEnabled(container.potionInv.getStackInSlot(0) != null); + } + + @Override + public void actionPerformed(String ident, Object... params) { + if (ident.equals("ampDown")) + amplifier--; + else if (ident.equals("ampUp")) + amplifier++; + applyEffect(); + validateInputButtons(); + } +} diff --git a/src/codechicken/nei/HUDRenderer.java b/src/codechicken/nei/HUDRenderer.java new file mode 100644 index 00000000..dd2042de --- /dev/null +++ b/src/codechicken/nei/HUDRenderer.java @@ -0,0 +1,96 @@ +package codechicken.nei; + +import codechicken.lib.config.ConfigTag; +import codechicken.nei.KeyManager.IKeyStateTracker; +import codechicken.nei.api.ItemInfo; +import codechicken.nei.guihook.GuiContainerManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.item.ItemStack; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.world.World; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import static codechicken.lib.gui.GuiDraw.*; + +public class HUDRenderer implements IKeyStateTracker +{ + @Override + public void tickKeyStates() { + if (KeyManager.keyStates.get("world.highlight_tips").down) { + ConfigTag tag = NEIClientConfig.getSetting("world.highlight_tips"); + tag.setBooleanValue(!tag.getBooleanValue()); + } + } + + public static void renderOverlay() { + Minecraft mc = Minecraft.getMinecraft(); + if (mc.currentScreen == null && + mc.theWorld != null && + !mc.gameSettings.keyBindPlayerList.getIsKeyPressed() && + NEIClientConfig.getBooleanSetting("world.highlight_tips") && + mc.objectMouseOver != null && + mc.objectMouseOver.typeOfHit == MovingObjectType.BLOCK) { + World world = mc.theWorld; + ArrayList items = ItemInfo.getIdentifierItems(world, mc.thePlayer, mc.objectMouseOver); + if (items.isEmpty()) + return; + + int minDamage = Integer.MAX_VALUE; + ItemStack stack = null; + for(ItemStack astack : items) { + if(astack.getItem() != null && astack.getItemDamage() < minDamage) { + stack = astack; + minDamage = stack.getItemDamage(); + } + } + + renderOverlay(stack, ItemInfo.getText(stack, world, mc.thePlayer, mc.objectMouseOver), getPositioning()); + } + } + + public static void renderOverlay(ItemStack stack, List textData, Point pos) { + int w = 0; + for (String s : textData) + w = Math.max(w, getStringWidth(s) + 29); + int h = Math.max(24, 10 + 10 * textData.size()); + + Dimension size = displaySize(); + int x = (size.width - w - 1) * pos.x / 10000; + int y = (size.height - h - 1) * pos.y / 10000; + + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + RenderHelper.disableStandardItemLighting(); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + + drawTooltipBox(x, y, w, h); + + int ty = (h - 8 * textData.size()) / 2; + for (int i = 0; i < textData.size(); i++) + drawString(textData.get(i), x + 24, y + ty + 10 * i, 0xFFA0A0A0, true); + + RenderHelper.enableGUIStandardItemLighting(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + + if (stack.getItem() != null) + GuiContainerManager.drawItem(x + 5, y + h / 2 - 8, stack); + } + + private static Point getPositioning() { + return new Point( + NEIClientConfig.getSetting("world.highlight_tips.x").getIntValue(), + NEIClientConfig.getSetting("world.highlight_tips.y").getIntValue()); + } + + public static void load() { + KeyManager.trackers.add(new HUDRenderer()); + } +} \ No newline at end of file diff --git a/src/codechicken/nei/Image.java b/src/codechicken/nei/Image.java new file mode 100644 index 00000000..5634a4f7 --- /dev/null +++ b/src/codechicken/nei/Image.java @@ -0,0 +1,17 @@ +package codechicken.nei; + +public class Image +{ + public int x; + public int y; + public int width; + public int height; + + public Image(int i, int j, int k, int l) + { + x = i; + y = j; + width = k; + height = l; + } +} diff --git a/src/codechicken/nei/InfiniteStackSizeHandler.java b/src/codechicken/nei/InfiniteStackSizeHandler.java new file mode 100644 index 00000000..a38a5a8a --- /dev/null +++ b/src/codechicken/nei/InfiniteStackSizeHandler.java @@ -0,0 +1,55 @@ +package codechicken.nei; + +import codechicken.lib.inventory.InventoryUtils; +import codechicken.nei.api.IInfiniteItemHandler; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; + +public class InfiniteStackSizeHandler implements IInfiniteItemHandler +{ + @Override + public void onPickup(ItemStack heldItem) + { + heldItem.stackSize = 1; + } + + @Override + public void onPlaceInfinite(ItemStack heldItem) + { + heldItem.stackSize = 111; + } + + @Override + public boolean canHandleItem(ItemStack stack) + { + return !stack.isItemStackDamageable(); + } + + @Override + public boolean isItemInfinite(ItemStack stack) + { + return stack.stackSize == -1 || stack.stackSize > 100; + } + + @Override + public void replenishInfiniteStack(InventoryPlayer inv, int slotNo) + { + ItemStack stack = inv.getStackInSlot(slotNo); + stack.stackSize = 111; + + for(int i = 0; i < inv.getSizeInventory(); i++) + { + if(i == slotNo) + continue; + + if(NEIServerUtils.areStacksSameType(stack, inv.getStackInSlot(i))) + inv.setInventorySlotContents(i, null); + } + } + + @Override + public ItemStack getInfiniteItem(ItemStack typeStack) + { + return InventoryUtils.copyStack(typeStack, -1); + } +} diff --git a/src/codechicken/nei/InfiniteToolHandler.java b/src/codechicken/nei/InfiniteToolHandler.java new file mode 100644 index 00000000..edf4b268 --- /dev/null +++ b/src/codechicken/nei/InfiniteToolHandler.java @@ -0,0 +1,38 @@ +package codechicken.nei; + +import codechicken.nei.api.IInfiniteItemHandler; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; + +public class InfiniteToolHandler implements IInfiniteItemHandler +{ + @Override + public void onPickup(ItemStack heldItem) { + heldItem.setItemDamage(0); + } + + @Override + public void onPlaceInfinite(ItemStack heldItem) { + heldItem.setItemDamage(-32000); + } + + @Override + public void replenishInfiniteStack(InventoryPlayer inv, int slotNo) { + inv.getStackInSlot(slotNo).setItemDamage(-32000); + } + + @Override + public boolean canHandleItem(ItemStack stack) { + return stack.getItem().isDamageable() && stack.getMaxStackSize() == 1; + } + + @Override + public boolean isItemInfinite(ItemStack stack) { + return stack.getItemDamage() < -30000; + } + + @Override + public ItemStack getInfiniteItem(ItemStack typeStack) { + return new ItemStack(typeStack.getItem(), 1, -32000); + } +} diff --git a/src/codechicken/nei/InventoryCraftingDummy.java b/src/codechicken/nei/InventoryCraftingDummy.java new file mode 100644 index 00000000..11866f22 --- /dev/null +++ b/src/codechicken/nei/InventoryCraftingDummy.java @@ -0,0 +1,20 @@ +package codechicken.nei; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.InventoryCrafting; + +public class InventoryCraftingDummy extends InventoryCrafting +{ + public InventoryCraftingDummy() + { + super(new Container() + { + @Override + public boolean canInteractWith(EntityPlayer entityplayer) + { + return true; + } + }, 3, 3); + } +} diff --git a/src/codechicken/nei/ItemList.java b/src/codechicken/nei/ItemList.java new file mode 100644 index 00000000..138b0766 --- /dev/null +++ b/src/codechicken/nei/ItemList.java @@ -0,0 +1,370 @@ +package codechicken.nei; + +import codechicken.nei.ItemPanel.ItemPanelObject; +import codechicken.nei.api.ItemInfo; +import codechicken.nei.guihook.GuiContainerManager; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IIcon; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class ItemList +{ + public static List items = new ArrayList(); + public static ListMultimap itemMap = ArrayListMultimap.create(); + + private static boolean matching = false; + private static boolean loading = false; + private static boolean research = false; + private static boolean reload = false; + private static HashSet erroredItems = new HashSet(); + + private static interface ItemMatcher + { + public boolean matches(ItemStack item); + } + + /*private static class SubsetItemMatcher implements ItemMatcher + { + final List tags; + + public SubsetItemMatcher(List tags) + { + this.tags = tags; + } + + @Override + public boolean matches(ItemStack item) + { + for(SubSetRangeTag tag : tags) + if(tag.isItemInRange(item.getItem(), item.getItemDamage())) + return true; + return false; + } + }*/ + + private static class PatternItemMatcher implements ItemMatcher + { + Pattern pattern; + + public PatternItemMatcher(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean matches(ItemStack item) { + return pattern.matcher(EnumChatFormatting.getTextWithoutFormattingCodes(GuiContainerManager.concatenatedDisplayName(item, true).toLowerCase())).find(); + } + } + + private static class EverythingItemMatcher implements ItemMatcher + { + @Override + public boolean matches(ItemStack item) { + return true; + } + } + + private static class NothingItemMatcher implements ItemMatcher + { + @Override + public boolean matches(ItemStack item) { + return false; + } + } + + public static ItemMatcher getSearchMatcher() { + String matchstring = NEIClientConfig.getSearchExpression().toLowerCase(); + /*if(matchstring.startsWith("@") && matchstring.length() > 1) + { + LinkedList tags = new LinkedList(); + try + { + String pattern = matchstring.substring(1); + for(SubSetRangeTag tag : DropDownFile.dropDownInstance.allTags()) + if(CommonUtils.filterText(tag.qualifiedname).toLowerCase().contains(pattern)) + tags.add(tag); + if(tags.isEmpty()) + return new NothingItemMatcher(); + + return new SubsetItemMatcher(tags); + } + catch(PatternSyntaxException e) + { + return new NothingItemMatcher(); + } + }*/ + + { + matchstring = matchstring.replace(".", ""); + matchstring = matchstring.replace("?", "."); + matchstring = matchstring.replace("*", ".+?"); + Pattern pattern = null; + try { + pattern = Pattern.compile(matchstring); + } catch (PatternSyntaxException ignored) {} + if (pattern == null || pattern.toString().equals("")) + return new EverythingItemMatcher(); + + return new PatternItemMatcher(pattern); + } + } + + public static boolean itemMatchesSearch(ItemStack item) { + return getSearchMatcher().matches(item); + } + + public static interface IItemCounter + { + public Item getItem(); + + public Thread getThread(); + } + + @SuppressWarnings("serial") + public static class TimeoutException extends RuntimeException + { + public final Item itemID; + + public TimeoutException(String msg, Item lastItem) { + super(msg); + itemID = lastItem; + } + } + + public static class ThreadLoadMonitor extends Thread + { + IItemCounter loadingThread; + + public ThreadLoadMonitor(IItemCounter handle) { + super("NEI Load Monitor"); + loadingThread = handle; + } + + @SuppressWarnings("deprecation") + @Override + public void run() { + if(true) return; + Item lastItem = null; + long lastTime = System.currentTimeMillis(); + while (loadingThread.getThread().isAlive()) { + if (lastItem != loadingThread.getItem()) { + lastTime = System.currentTimeMillis(); + lastItem = loadingThread.getItem(); + } else if (System.currentTimeMillis() - lastTime > 2000 && lastItem != null) { + loadingThread.getThread().stop(new TimeoutException("Took to long to advance item", lastItem)); + } + try { + Thread.sleep(2000); + } catch (InterruptedException ie) {} + } + } + } + + public static class ThreadMatchSearch extends Thread implements IItemCounter + { + private Item item; + + public ThreadMatchSearch() { + super("NEI Item Searching Thread"); + matching = true; + } + + @Override + public Item getItem() { + return item; + } + + @Override + public Thread getThread() { + return this; + } + + @Override + public void run() { + //new ThreadLoadMonitor(this).start(); + try { + startMatch: + while (matching) { + try { + ArrayList visibleitems = new ArrayList(); + ItemMatcher matcher = getSearchMatcher(); + for (ItemStack stack : items) { + item = stack.getItem(); + if (research) { + research = false; + continue startMatch; + } + + /*if(stack.hasTagCompound() ? NEIClientConfig.vishash.isItemHidden(item, stack.stackTagCompound) : NEIClientConfig.vishash.isItemHidden(item, stack.getItemDamage())) + continue;*/ + + if (NEIClientConfig.bannedBlocks.contains(stack)) + continue; + + if (matcher.matches(stack)) + visibleitems.add(new ItemPanelStack(stack)); + } + ItemPanel.visibleitems = visibleitems; + } catch (TimeoutException e) { + System.err.println("Removing itemID: " + e.itemID + " from list."); + e.printStackTrace(); + erroredItems.add(e.itemID); + loadItems(); + } + matching = false; + } + } + finally { + matching = false; + } + } + } + + private static HashSet stackTraces = new HashSet(); + + public static class ThreadLoadItems extends Thread implements IItemCounter + { + private Item item; + + public ThreadLoadItems() { + super("NEI Item Loading Thread"); + loading = true; + } + + @Override + public Item getItem() { + return item; + } + + @Override + public Thread getThread() { + return this; + } + + @Override + public void run() { + new ThreadLoadMonitor(this).start(); + startSearch: + while (loading) { + try { + ArrayList items = new ArrayList(); + ArrayList permutations = new ArrayList(); + ListMultimap itemMap = ArrayListMultimap.create(); + + for (Item item : (Iterable) Item.itemRegistry) { + this.item = item; + if (reload) { + reload = false; + continue startSearch; + } + + if (item == null || ItemInfo.isHidden(item)) + continue; + + permutations.clear(); + permutations.addAll(ItemInfo.getItemOverrides(item)); + if(permutations.isEmpty()) { + try { + item.getSubItems(item, null, permutations); + } catch (Exception e) { + System.err.println("Removing itemID: " + item + " from list."); + e.printStackTrace(); + erroredItems.add(item); + continue; + } + } + if(permutations.isEmpty()) { + HashSet damageIconSet = new HashSet(); + for (int damage = 0; damage < 16; damage++) { + ItemStack itemstack = new ItemStack(item, 1, damage); + try { + IIcon icon = item.getIconIndex(itemstack); + String name = GuiContainerManager.concatenatedDisplayName(itemstack, false); + String s = name + "@" + (icon == null ? 0 : icon.hashCode()); + if (!damageIconSet.contains(s)) { + damageIconSet.add(s); + permutations.add(itemstack); + } + + } catch (Throwable t) { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + String stackTrace = itemstack + sw.toString(); + if (!stackTraces.contains(stackTrace)) { + System.err.println("NEI: Omitting #" + this.item + ":" + damage + " " + item.getClass().getSimpleName()); + t.printStackTrace(); + stackTraces.add(stackTrace); + } + } + } + } + + for(Iterator it = permutations.iterator(); it.hasNext(); ) + if(ItemInfo.isHidden(it.next())) + it.remove(); + + items.addAll(permutations); + itemMap.putAll(item, permutations); + } + + item = null; + + /*DropDownFile dropDownInstance = DropDownFile.dropDownInstance; + dropDownInstance.resetHashes(); + for(ItemStack stack : items) + { + if(reload) + { + reload = false; + continue startSearch; + } + dropDownInstance.addItemIfInRange(stack.getItem(), stack.getItemDamage(), stack.stackTagCompound); + } + dropDownInstance.updateState();*/ + ItemList.items = items; + ItemList.itemMap = itemMap; + if (reload) { + reload = false; + continue; + } + loading = false; + } catch (TimeoutException e) { + System.err.println("Removing itemID: " + e.itemID + " from list."); + e.printStackTrace(); + erroredItems.add(e.itemID); + } + } + updateSearch(); + } + } + + public static boolean isMatching() { + return matching; + } + + public static void updateSearch() { + if (matching) + research = true; + else + new ThreadMatchSearch().start(); + } + + public static void loadItems() { + if (loading) + reload = true; + else + new ThreadLoadItems().start(); + } +} diff --git a/src/codechicken/nei/ItemMobSpawner.java b/src/codechicken/nei/ItemMobSpawner.java new file mode 100644 index 00000000..11301a34 --- /dev/null +++ b/src/codechicken/nei/ItemMobSpawner.java @@ -0,0 +1,139 @@ +package codechicken.nei; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.monster.IMob; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntityMobSpawner; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; +import net.minecraftforge.client.MinecraftForgeClient; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ItemMobSpawner extends ItemBlock +{ + private static Map entityHashMap; + private static Map IDtoNameMap; + public static int idPig; + private static boolean loaded; + + public ItemMobSpawner(World world) { + super(Blocks.mob_spawner); + + hasSubtypes = true; + MinecraftForgeClient.registerItemRenderer(this, new SpawnerRenderer()); + + entityHashMap = new HashMap(); + IDtoNameMap = new HashMap(); + loadSpawners(world); + } + + /** + * These are ASM translated from BlockMobSpawner + */ + public static int placedX; + public static int placedY; + public static int placedZ; + + @Override + public IIcon getIconFromDamage(int par1) { + return Blocks.mob_spawner.getBlockTextureFromSide(0); + } + + public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int par7, float par8, float par9, float par10) { + if (super.onItemUse(itemstack, entityplayer, world, x, y, z, par7, par8, par9, par10) && world.isRemote) { + TileEntityMobSpawner tileentitymobspawner = (TileEntityMobSpawner) world.getTileEntity(placedX, placedY, placedZ); + if (tileentitymobspawner != null) { + setDefaultTag(itemstack); + String mobtype = IDtoNameMap.get(itemstack.getItemDamage()); + if (mobtype != null) { + NEICPH.sendMobSpawnerID(placedX, placedY, placedZ, mobtype); + tileentitymobspawner.func_145881_a().setEntityName(mobtype); + } + } + return true; + } + return false; + } + + @Override + public void addInformation(ItemStack itemstack, EntityPlayer par2EntityPlayer, List list, boolean par4) { + setDefaultTag(itemstack); + int meta = itemstack.getItemDamage(); + if (meta == 0) { + meta = idPig; + } + Entity e = getEntity(meta); + if (e == null) return; + list.add("\u00A7" + (e instanceof IMob ? "4" : "3") + IDtoNameMap.get(meta)); + } + + public static EntityLiving getEntity(int ID) { + EntityLiving entityliving = entityHashMap.get(ID); + if (entityliving == null) { + World world = NEIClientUtils.mc().theWorld; + loadSpawners(world); + try { + Class class1 = (Class) EntityList.IDtoClassMapping.get(ID); + if (class1 != null && EntityLiving.class.isAssignableFrom(class1)) { + entityliving = (EntityLiving) class1.getConstructor(new Class[]{World.class}).newInstance(new Object[]{world}); + } + } catch (Throwable t) { + t.printStackTrace(); + } + entityHashMap.put(ID, entityliving); + } + return entityliving; + } + + private void setDefaultTag(ItemStack itemstack) { + if (!IDtoNameMap.containsKey(itemstack.getItemDamage())) + itemstack.setItemDamage(idPig); + } + + public static void loadSpawners(World world) { + if (loaded) return; + loaded = true; + try { + HashMap, String> classToStringMapping = (HashMap, String>) EntityList.classToStringMapping; + HashMap, Integer> classToIDMapping = (HashMap, Integer>) EntityList.classToIDMapping; + for (Class eclass : classToStringMapping.keySet()) { + if (!EntityLiving.class.isAssignableFrom(eclass)) + continue; + try { + EntityLiving entityliving = (EntityLiving) eclass.getConstructor(new Class[]{World.class}).newInstance(new Object[]{world}); + entityliving.isChild(); + + int id = classToIDMapping.get(eclass); + String name = classToStringMapping.get(eclass); + + if (name.equals("EnderDragon")) + continue; + + IDtoNameMap.put(id, name); + + if (name.equals("Pig")) + idPig = id; + } catch (Throwable t) { + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + + @Override + public void getSubItems(Item item, CreativeTabs tab, List list) { + for(int i : IDtoNameMap.keySet()) + list.add(new ItemStack(item, 1, i)); + } +} diff --git a/src/codechicken/nei/ItemPanel.java b/src/codechicken/nei/ItemPanel.java new file mode 100644 index 00000000..40644797 --- /dev/null +++ b/src/codechicken/nei/ItemPanel.java @@ -0,0 +1,381 @@ +package codechicken.nei; + +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.api.GuiInfo; +import codechicken.nei.api.INEIGuiHandler; +import codechicken.nei.guihook.GuiContainerManager; +import codechicken.nei.recipe.GuiCraftingRecipe; +import codechicken.nei.recipe.GuiRecipe; +import codechicken.nei.recipe.GuiUsageRecipe; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +import static codechicken.lib.gui.GuiDraw.drawRect; + +public class ItemPanel extends Widget +{ + public class ItemPanelSlot + { + public ItemPanelObject contents; + public int slotIndex; + + public ItemPanelSlot(int index) + { + contents = visibleitems.get(index); + slotIndex = index; + } + + public ItemStack getItemStack() + { + return contents instanceof ItemPanelStack ? ((ItemPanelStack)contents).item : null; + } + } + + public static interface ItemPanelObject + { + void draw(int x, int y); + List handleTooltip(List tooltip); + } + + public void resize() + { + marginLeft = x + (width % 18) / 2; + marginTop = y + (height % 18) / 2; + columns = width / 18; + rows = height / 18; + + calculatePage(); + updateValidSlots(); + } + + private void calculatePage() { + if(itemsPerPage == 0) + numPages = 0; + else + numPages = (int)Math.ceil((float)visibleitems.size() / (float)itemsPerPage); + + if(firstIndex >= visibleitems.size()) + firstIndex = 0; + + if(numPages == 0) + page = 0; + else + page = firstIndex/itemsPerPage+1; + } + + private void updateValidSlots() { + GuiContainer gui = NEIClientUtils.getGuiContainer(); + validSlotMap = new boolean[rows*columns]; + itemsPerPage = 0; + for(int i = 0; i < validSlotMap.length; i++) + if(slotValid(gui, i)) { + validSlotMap[i] = true; + itemsPerPage++; + } + } + + private boolean slotValid(GuiContainer gui, int i) { + Rectangle4i rect = getSlotRect(i); + for(INEIGuiHandler handler : GuiInfo.guiHandlers) + if(handler.hideItemPanelSlot(gui, rect.x, rect.y, rect.w, rect.h)) + return false; + return true; + } + + public Rectangle4i getSlotRect(int i) { + return getSlotRect(i/columns, i%columns); + } + + public Rectangle4i getSlotRect(int row, int column) { + return new Rectangle4i(marginLeft+column*18, marginTop+row*18, 18, 18); + } + + @Override + public void draw(int mousex, int mousey) + { + if(itemsPerPage == 0) + return; + + GuiContainerManager.enableMatrixStackLogging(); + int index = firstIndex; + for(int i = 0; i < rows*columns && index < visibleitems.size(); i++) { + if(validSlotMap[i]) { + Rectangle4i rect = getSlotRect(i); + if(rect.contains(mousex, mousey)) + drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555);//highlight + + visibleitems.get(index).draw(rect.x, rect.y); + + index++; + } + } + GuiContainerManager.disableMatrixStackLogging(); + } + + @Override + public void postDraw(int mousex, int mousey) + { + if(draggedStack != null) + { + GuiContainerManager.drawItems.zLevel += 100; + GuiContainerManager.drawItem(mousex - 8, mousey - 8, draggedStack); + GuiContainerManager.drawItems.zLevel -= 100; + } + } + + @Override + public void mouseDragged(int mousex, int mousey, int button, long heldTime) + { + if(mouseDownSlot >= 0 && draggedStack == null && NEIClientUtils.getHeldItem() == null) + { + ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey); + ItemStack stack = new ItemPanelSlot(mouseDownSlot).getItemStack(); + if(stack != null && (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 500)) + { + int amount = NEIClientConfig.getItemQuantity(); + if(amount == 0) + amount = stack.getMaxStackSize(); + + draggedStack = NEIServerUtils.copyStack(stack, amount); + } + } + } + + @Override + public boolean handleClick(int mousex, int mousey, int button) + { + if(handleDraggedClick(mousex, mousey, button)) + return true; + + if(NEIClientUtils.getHeldItem() != null) + { + for(INEIGuiHandler handler : GuiInfo.guiHandlers) + if(handler.hideItemPanelSlot(NEIClientUtils.getGuiContainer(), mousex, mousey, 1, 1)) + return false; + + if(NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")) + { + if(button == 1) + NEIClientUtils.decreaseSlotStack(-999); + else + NEIClientUtils.deleteHeldItem(); + } + else + { + NEIClientUtils.dropHeldItem(); + } + return true; + } + + ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey); + if(hoverSlot != null) + { + if(button == 2) + { + ItemStack stack = hoverSlot.getItemStack(); + if(stack != null) + { + int amount = NEIClientConfig.getItemQuantity(); + if(amount == 0) + amount = stack.getMaxStackSize(); + + draggedStack = NEIServerUtils.copyStack(stack, amount); + } + } + else + { + mouseDownSlot = hoverSlot.slotIndex; + } + return true; + } + return false; + } + + private boolean handleDraggedClick(int mousex, int mousey, int button) + { + if(draggedStack == null) + return false; + + GuiContainer gui = NEIClientUtils.getGuiContainer(); + boolean handled = false; + for(INEIGuiHandler handler : GuiInfo.guiHandlers) + if(handler.handleDragNDrop(gui, mousex, mousey, draggedStack, button)) + { + handled = true; + if(draggedStack.stackSize == 0) + { + draggedStack = null; + return true; + } + } + + if(handled) + return true; + + Slot overSlot = gui.getSlotAtPosition(mousex, mousey); + if(overSlot != null && overSlot.isItemValid(draggedStack)) + { + if(NEIClientConfig.canPerformAction("item")) + { + int contents = overSlot.getHasStack() ? overSlot.getStack().stackSize : 0; + int add = button == 0 ? draggedStack.stackSize : 1; + if(overSlot.getHasStack() && !NEIServerUtils.areStacksSameType(draggedStack, overSlot.getStack())) + contents = 0; + int total = Math.min(contents+add, Math.min(overSlot.getSlotStackLimit(), draggedStack.getMaxStackSize())); + + if(total > contents) + { + NEIClientUtils.setSlotContents(overSlot.slotNumber, NEIServerUtils.copyStack(draggedStack, total), true); + NEICPH.sendGiveItem(NEIServerUtils.copyStack(draggedStack, total), false, false); + draggedStack.stackSize-=total-contents; + } + if(draggedStack.stackSize == 0) + draggedStack = null; + } + else + { + draggedStack = null; + } + } + else if(mousex < gui.guiLeft || mousey < gui.guiTop || mousex >= gui.guiLeft+gui.xSize || mousey >= gui.guiTop+gui.ySize) + { + draggedStack = null; + } + + return true; + } + + @Override + public boolean handleClickExt(int mousex, int mousey, int button) + { + return handleDraggedClick(mousex, mousey, button); + } + + @Override + public void mouseUp(int mousex, int mousey, int button) + { + ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey); + if(hoverSlot != null && hoverSlot.slotIndex == mouseDownSlot && hoverSlot.getItemStack() != null && draggedStack == null) + { + ItemStack item = hoverSlot.getItemStack(); + if(NEIController.manager.window instanceof GuiRecipe || !NEIClientConfig.canPerformAction("item")) + { + if(button == 0) + GuiCraftingRecipe.openRecipeGui("item", item); + else if(button == 1) + GuiUsageRecipe.openRecipeGui("item", item); + + draggedStack = null; + mouseDownSlot = -1; + return; + } + + NEIClientUtils.cheatItem(item, button, -1); + } + + mouseDownSlot = -1; + } + + @Override + public boolean onMouseWheel(int i, int mousex, int mousey) + { + if(!contains(mousex, mousey)) + return false; + + scroll(-i); + return true; + } + + @Override + public boolean handleKeyPress(int keyID, char keyChar) + { + if(keyID == NEIClientConfig.getKeyBinding("gui.next")) + { + scroll(1); + return true; + } + if(keyID == NEIClientConfig.getKeyBinding("gui.prev")) + { + scroll(-1); + return true; + } + return false; + } + + @Override + public ItemStack getStackMouseOver(int mousex, int mousey) + { + ItemPanelSlot slot = getSlotMouseOver(mousex, mousey); + return slot == null ? null : slot.getItemStack(); + } + + public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) + { + int index = firstIndex; + for(int i = 0; i < rows*columns && index < visibleitems.size(); i++) + if(validSlotMap[i]) { + if(getSlotRect(i).contains(mousex, mousey)) + return new ItemPanelSlot(index); + index++; + } + + return null; + } + + @Override + public List handleTooltip(int mx, int my, List tooltip) + { + if(getSlotMouseOver(mx, my) == null) + return tooltip; + + return getSlotMouseOver(mx, my).contents.handleTooltip(tooltip); + } + + public void scroll(int i) + { + if(itemsPerPage != 0) { + int oldIndex = firstIndex; + firstIndex += i*itemsPerPage; + if(firstIndex >= visibleitems.size()) + firstIndex = 0; + if(firstIndex < 0) + if(oldIndex > 0) + firstIndex = 0; + else + firstIndex = (visibleitems.size()-1)/itemsPerPage*itemsPerPage; + + calculatePage(); + } + } + + public int getPage() + { + return page; + } + + public int getNumPages() + { + return numPages; + } + + public static ArrayList visibleitems = new ArrayList(); + + public ItemStack draggedStack; + public int mouseDownSlot = -1; + + private int marginLeft; + private int marginTop; + private int rows; + private int columns; + + private boolean[] validSlotMap; + private int firstIndex; + private int itemsPerPage; + + private int page; + private int numPages; +} diff --git a/src/codechicken/nei/ItemPanelStack.java b/src/codechicken/nei/ItemPanelStack.java new file mode 100644 index 00000000..408c02d7 --- /dev/null +++ b/src/codechicken/nei/ItemPanelStack.java @@ -0,0 +1,29 @@ +package codechicken.nei; + +import codechicken.nei.ItemPanel.ItemPanelObject; +import codechicken.nei.guihook.GuiContainerManager; +import net.minecraft.item.ItemStack; + +import java.util.List; + +public class ItemPanelStack implements ItemPanelObject +{ + public ItemStack item; + + public ItemPanelStack(ItemStack itemstack) + { + item = itemstack; + } + + @Override + public void draw(int x, int y) + { + GuiContainerManager.drawItem(x+1, y+1, item); + } + + @Override + public List handleTooltip(List tooltip) + { + return tooltip; + } +} diff --git a/src/codechicken/nei/ItemQuantityField.java b/src/codechicken/nei/ItemQuantityField.java new file mode 100644 index 00000000..5bd8edbe --- /dev/null +++ b/src/codechicken/nei/ItemQuantityField.java @@ -0,0 +1,56 @@ +package codechicken.nei; + +public class ItemQuantityField extends TextField +{ + public ItemQuantityField(String ident) + { + super(ident); + centered = true; + } + + @Override + public boolean isValid(String string) + { + if(string.equals("")) + return true; + + try + { + return Integer.parseInt(string) >= 0; + } + catch(NumberFormatException nfe) + { + return false; + } + } + + public int intValue() + { + return intValue(text()); + } + + public int intValue(String s) + { + try + { + return Integer.parseInt(s); + } + catch(NumberFormatException nfe) + { + return 0; + } + } + + @Override + public void loseFocus() + { + setText(Integer.toString(NEIClientConfig.getItemQuantity())); + } + + @Override + public void onTextChange(String oldText) + { + if(intValue(oldText) != intValue())//hacky recursion stopper + NEIClientUtils.setItemQuantity(intValue()); + } +} diff --git a/src/codechicken/nei/ItemStackMap.java b/src/codechicken/nei/ItemStackMap.java new file mode 100644 index 00000000..6dd10b45 --- /dev/null +++ b/src/codechicken/nei/ItemStackMap.java @@ -0,0 +1,230 @@ +package codechicken.nei; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import java.util.*; + +import static codechicken.lib.inventory.InventoryUtils.actualDamage; +import static codechicken.lib.inventory.InventoryUtils.newItemStack; +import static net.minecraftforge.oredict.OreDictionary.WILDCARD_VALUE; + +/** + * A maplike class for ItemStack keys with wildcard damage/NBT. Optimised for lookup + */ +public class ItemStackMap +{ + public static class StackMetaKey + { + public int damage; + public NBTTagCompound tag; + + public StackMetaKey(int damage, NBTTagCompound tag) { + this.damage = damage; + this.tag = tag; + } + + public StackMetaKey(ItemStack key) { + this(actualDamage(key), key.stackTagCompound); + } + + public int hashCode() { + return damage*31+Objects.hashCode(tag); + } + + public boolean equals(Object o) { + if(!(o instanceof StackMetaKey)) + return false; + StackMetaKey t = (StackMetaKey)o; + return damage == t.damage && Objects.equals(tag, t.tag); + } + } + + public static class Entry + { + public ItemStack key; + public T value; + + public Entry(ItemStack key, T value) { + this.key = key; + this.value = value; + } + } + + public class DetailMap + { + public T wildcard; + public HashMap damageMap; + public HashMap tagMap; + public HashMap metaMap; + + public T get(ItemStack key) { + if(wildcard != null) + return wildcard; + + if(damageMap != null) { + T ret = damageMap.get(actualDamage(key)); + if(ret != null) return ret; + } + if(tagMap != null) { + T ret = tagMap.get(key.stackTagCompound); + if(ret != null) return ret; + } + if(metaMap != null) + return metaMap.get(new StackMetaKey(key)); + + return null; + } + + public void put(ItemStack key, T value) { + switch(getKeyType(actualDamage(key), key.stackTagCompound)) { + case 0: + if(metaMap == null) metaMap = new HashMap(); + metaMap.put(new StackMetaKey(key), value); + break; + case 1: + if(tagMap == null) tagMap = new HashMap(); + tagMap.put(key.stackTagCompound, value); + break; + case 2: + if(damageMap == null) damageMap = new HashMap(); + damageMap.put(actualDamage(key), value); + break; + case 3: + wildcard = value; + break; + } + } + + public T remove(ItemStack key) { + switch(getKeyType(actualDamage(key), key.stackTagCompound)) { + case 0: + return metaMap != null ? metaMap.remove(new StackMetaKey(key)) : null; + case 1: + return tagMap != null ? tagMap.remove(key.stackTagCompound) : null; + case 2: + return damageMap != null ? damageMap.remove(actualDamage(key)) : null; + case 3: + T ret = wildcard; + wildcard = null; + return ret; + } + return null; + } + + public void addKeys(Item item, List list) { + if(wildcard != null) + list.add(newItemStack(item, 1, WILDCARD_VALUE, WILDCARD_TAG)); + if(damageMap != null) + for(int damage : damageMap.keySet()) + list.add(newItemStack(item, 1, damage, WILDCARD_TAG)); + if(tagMap != null) + for(NBTTagCompound tag : tagMap.keySet()) + list.add(newItemStack(item, 1, WILDCARD_VALUE, tag)); + if(metaMap != null) + for(StackMetaKey key : metaMap.keySet()) + list.add(newItemStack(item, 1, key.damage, key.tag)); + } + + public void addValues(LinkedList list) { + if(wildcard != null) + list.add(wildcard); + if(damageMap != null) + list.addAll(damageMap.values()); + if(tagMap != null) + list.addAll(tagMap.values()); + if(metaMap != null) + list.addAll(metaMap.values()); + } + + public void addEntries(Item item, LinkedList> list) { + if(wildcard != null) + list.add(new Entry(newItemStack(item, 1, WILDCARD_VALUE, WILDCARD_TAG), wildcard)); + if(damageMap != null) + for(Map.Entry entry : damageMap.entrySet()) + list.add(new Entry(newItemStack(item, 1, entry.getKey(), WILDCARD_TAG), entry.getValue())); + if(tagMap != null) + for(Map.Entry entry : tagMap.entrySet()) + list.add(new Entry(newItemStack(item, 1, WILDCARD_VALUE, entry.getKey()), entry.getValue())); + if(metaMap != null) + for(Map.Entry entry : metaMap.entrySet()) + list.add(new Entry(newItemStack(item, 1, entry.getKey().damage, entry.getKey().tag), entry.getValue())); + } + } + + public static int getKeyType(int damage, NBTTagCompound tag) { + int i = 0; + if(isWildcard(damage)) i = 1; + if(isWildcard(tag)) i |= 2; + return i; + } + + public static boolean isWildcard(int damage) { + return damage == WILDCARD_VALUE; + } + + public static boolean isWildcard(NBTTagCompound tag) { + return tag != null && tag.getBoolean("*"); + } + + public static NBTTagCompound WILDCARD_TAG; + + static { + WILDCARD_TAG = new NBTTagCompound(); + WILDCARD_TAG.setBoolean("*", true); + } + + protected HashMap itemMap = new HashMap(); + + public T get(ItemStack key) { + if(key == null || key.getItem() == null) + return null; + + DetailMap map = itemMap.get(key.getItem()); + return map == null ? null : map.get(key); + } + + public void put(ItemStack key, T value) { + if(key == null || key.getItem() == null) + return; + + DetailMap map = itemMap.get(key.getItem()); + if(map == null) + itemMap.put(key.getItem(), map = new DetailMap()); + map.put(key, value); + } + + public void clear() { + itemMap.clear(); + } + + public T remove(ItemStack key) { + if (key == null || key.getItem() == null) + return null; + + DetailMap map = itemMap.get(key.getItem()); + return map == null ? null : map.remove(key); + } + + public List keys() { + LinkedList list = new LinkedList(); + for(Map.Entry entry : itemMap.entrySet()) + entry.getValue().addKeys(entry.getKey(), list); + return list; + } + + public List values() { + LinkedList list = new LinkedList(); + for(DetailMap map : itemMap.values()) + map.addValues(list); + return list; + } + + public List> entries() { + LinkedList> list = new LinkedList>(); + for(Map.Entry entry : itemMap.entrySet()) + entry.getValue().addEntries(entry.getKey(), list); + return list; + } +} diff --git a/src/codechicken/nei/ItemStackSet.java b/src/codechicken/nei/ItemStackSet.java new file mode 100644 index 00000000..04a517e7 --- /dev/null +++ b/src/codechicken/nei/ItemStackSet.java @@ -0,0 +1,35 @@ +package codechicken.nei; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import java.util.List; + +public class ItemStackSet +{ + ItemStackMap underlying = new ItemStackMap(); + + public void add(ItemStack item) { + underlying.put(item, new Object()); + } + + public boolean remove(ItemStack item) { + return underlying.remove(item) != null; + } + + public boolean contains(ItemStack item) { + return underlying.get(item) != null; + } + + public boolean containsAll(Item item) { + return underlying.itemMap.containsKey(item) && underlying.itemMap.get(item).wildcard != null; + } + + public List values() { + return underlying.keys(); + } + + public void clear() { + underlying.clear(); + } +} diff --git a/src/codechicken/nei/KeyManager.java b/src/codechicken/nei/KeyManager.java new file mode 100644 index 00000000..9e39fea6 --- /dev/null +++ b/src/codechicken/nei/KeyManager.java @@ -0,0 +1,52 @@ +package codechicken.nei; + +import org.lwjgl.input.Keyboard; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map.Entry; + +/** + * Good old down/held/up keystate tracker + */ +public class KeyManager +{ + public static interface IKeyStateTracker + { + public void tickKeyStates(); + } + + public static class KeyState + { + public boolean down; + public boolean held; + public boolean up; + } + + public static HashMap keyStates = new HashMap(); + public static LinkedList trackers = new LinkedList(); + + public static void tickKeyStates() + { + for(Entry entry : keyStates.entrySet()) + { + int keyCode = NEIClientConfig.getKeyBinding(entry.getKey()); + boolean down = keyCode != 0 && Keyboard.isKeyDown(keyCode); + KeyState state = entry.getValue(); + if(down) + { + state.down = !state.held; + state.up = false; + } + else + { + state.up = state.held; + state.down = false; + } + state.held = down; + } + + for(IKeyStateTracker tracker : trackers) + tracker.tickKeyStates(); + } +} diff --git a/src/codechicken/nei/Label.java b/src/codechicken/nei/Label.java new file mode 100644 index 00000000..2ba5e6e4 --- /dev/null +++ b/src/codechicken/nei/Label.java @@ -0,0 +1,32 @@ +package codechicken.nei; + +import static codechicken.lib.gui.GuiDraw.drawString; +import static codechicken.lib.gui.GuiDraw.drawStringC; + +public class Label extends Widget +{ + boolean centered; + int colour; + String text; + + public Label(String s, boolean center, int color) + { + text = s; + centered = center; + colour = color; + } + + public Label(String s, boolean center) + { + this(s, center, 0xFFFFFFFF); + } + + @Override + public void draw(int mousex, int mousey) + { + if(centered) + drawStringC(text, x, y, colour); + else + drawString(text, x, y, colour); + } +} diff --git a/src/codechicken/nei/LangProxy.java b/src/codechicken/nei/LangProxy.java new file mode 100644 index 00000000..d7bb9036 --- /dev/null +++ b/src/codechicken/nei/LangProxy.java @@ -0,0 +1,19 @@ +package codechicken.nei; + +import net.minecraft.util.StatCollector; + +public class LangProxy +{ + public final String namespace; + + public LangProxy(String namespace) { + this.namespace = namespace+"."; + } + + public String translate(String key) { + return StatCollector.translateToLocal(namespace+key); + } + public String format(String key, Object... params) { + return StatCollector.translateToLocalFormatted(namespace+key, params); + } +} diff --git a/src/codechicken/nei/LayoutManager.java b/src/codechicken/nei/LayoutManager.java new file mode 100644 index 00000000..d400289f --- /dev/null +++ b/src/codechicken/nei/LayoutManager.java @@ -0,0 +1,811 @@ +package codechicken.nei; + +import codechicken.lib.gui.GuiDraw; +import codechicken.nei.KeyManager.IKeyStateTracker; +import codechicken.nei.api.*; +import codechicken.nei.guihook.*; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.gui.inventory.GuiContainerCreative; +import net.minecraft.client.renderer.InventoryEffectRenderer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import org.lwjgl.opengl.GL11; + +import java.util.HashMap; +import java.util.List; +import java.util.TreeSet; + +import static codechicken.lib.gui.GuiDraw.*; +import static codechicken.nei.NEIClientConfig.*; +import static codechicken.nei.NEIClientUtils.*; + +public class LayoutManager implements IContainerInputHandler, IContainerTooltipHandler, IContainerDrawHandler, IContainerObjectHandler, IKeyStateTracker +{ + private static LayoutManager instance; + + private static Widget inputFocused; + /** + * Sorted bottom first + */ + private static TreeSet drawWidgets; + /** + * Sorted top first + */ + private static TreeSet controlWidgets; + + public static ItemPanel itemPanel; + public static Button dropDown; + public static TextField searchField; + + public static Button options; + + public static Button prev; + public static Button next; + public static Label pageLabel; + public static Button more; + public static Button less; + public static ItemQuantityField quantity; + + public static SaveLoadButton[] stateButtons; + public static Button[] deleteButtons; + + public static Button delete; + public static ButtonCycled gamemode; + public static Button rain; + public static Button magnet; + public static Button[] timeButtons = new Button[4]; + public static Button heal; + + public static IRecipeOverlayRenderer overlayRenderer; + + public static HashMap layoutStyles = new HashMap(); + + public static void load() { + API.addLayoutStyle(0, new LayoutStyleMinecraft()); + API.addLayoutStyle(1, new LayoutStyleTMIOld()); + + instance = new LayoutManager(); + KeyManager.trackers.add(instance); + GuiContainerManager.addInputHandler(instance); + GuiContainerManager.addTooltipHandler(instance); + GuiContainerManager.addDrawHandler(instance); + GuiContainerManager.addObjectHandler(instance); + init(); + } + + @Override + public void onPreDraw(GuiContainer gui) { + if (!isHidden() && isEnabled() && gui instanceof InventoryEffectRenderer)//Reset the gui to the center of the screen, for potion effect offsets etc + { + gui.guiLeft = (gui.width - gui.xSize) / 2; + gui.guiTop = (gui.height - gui.ySize) / 2; + + if (gui instanceof GuiContainerCreative && gui.buttonList.size() >= 2) { + GuiButton button1 = (GuiButton) gui.buttonList.get(0); + GuiButton button2 = (GuiButton) gui.buttonList.get(1); + button1.xPosition = gui.guiLeft; + button2.xPosition = gui.guiLeft + gui.xSize - 20; + } + } + } + + @Override + public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) { + if (isHidden()) + return; + + for (Widget widget : controlWidgets) + widget.onGuiClick(mousex, mousey); + } + + @Override + public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) { + if (isHidden()) + return false; + + if (!isEnabled()) + return options.contains(mousex, mousey) && options.handleClick(mousex, mousey, button); + + for (Widget widget : controlWidgets) { + widget.onGuiClick(mousex, mousey); + if (widget.contains(mousex, mousey) ? widget.handleClick(mousex, mousey, button) : widget.handleClickExt(mousex, mousey, button)) + return true; + } + + return false; + } + + @Override + public boolean objectUnderMouse(GuiContainer gui, int mousex, int mousey) { + if (!isHidden() && isEnabled()) { + for (Widget widget : controlWidgets) + if (widget.contains(mousex, mousey)) + return true; + } + + return false; + } + + public boolean keyTyped(GuiContainer gui, char keyChar, int keyID) { + if (isEnabled() && !isHidden()) { + if (inputFocused != null) + return inputFocused.handleKeyPress(keyID, keyChar); + + for (Widget widget : controlWidgets) + if (widget.handleKeyPress(keyID, keyChar)) + return true; + } + + return false; + } + + @Override + public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) { + } + + @Override + public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) { + if (keyID == getKeyBinding("gui.hide")) { + toggleBooleanSetting("inventory.hidden"); + return true; + } + if (isEnabled() && !isHidden()) { + for (Widget widget : controlWidgets) + if (inputFocused == null) + widget.lastKeyTyped(keyID, keyChar); + } + return false; + } + + public void onMouseUp(GuiContainer gui, int mx, int my, int button) { + if (!isHidden() && isEnabled()) { + for (Widget widget : controlWidgets) + widget.mouseUp(mx, my, button); + } + } + + @Override + public void onMouseDragged(GuiContainer gui, int mx, int my, int button, long heldTime) { + if (!isHidden() && isEnabled()) { + for (Widget widget : controlWidgets) + widget.mouseDragged(mx, my, button, heldTime); + } + } + + @Override + public ItemStack getStackUnderMouse(GuiContainer gui, int mousex, int mousey) { + if (!isHidden() && isEnabled()) { + for (Widget widget : controlWidgets) { + ItemStack stack = widget.getStackMouseOver(mousex, mousey); + if (stack != null) + return stack; + } + } + return null; + } + + public void renderObjects(GuiContainer gui, int mousex, int mousey) { + if (!isHidden()) { + layout(gui); + if (isEnabled()) { + getLayoutStyle().drawBackground(GuiContainerManager.getManager(gui)); + for (Widget widget : drawWidgets) + widget.draw(mousex, mousey); + } else { + options.draw(mousex, mousey); + } + + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + } + } + + @Override + public void postRenderObjects(GuiContainer gui, int mousex, int mousey) { + if (!isHidden() && isEnabled()) { + for (Widget widget : drawWidgets) + widget.postDraw(mousex, mousey); + } + } + + @Override + public List handleTooltip(GuiContainer gui, int mousex, int mousey, List currenttip) { + if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) { + for (Widget widget : controlWidgets) + currenttip = widget.handleTooltip(mousex, mousey, currenttip); + } + return currenttip; + } + + @Override + public List handleItemDisplayName(GuiContainer gui, ItemStack stack, List currenttip) { + String overridename = ItemInfo.getNameOverride(stack); + if (overridename != null) + currenttip.set(0, overridename); + + String mainname = currenttip.get(0); + if (showIDs()) { + mainname += " " + Item.getIdFromItem(stack.getItem()); + if (stack.getItemDamage() != 0) + mainname += ":" + stack.getItemDamage(); + + currenttip.set(0, mainname); + } + + return currenttip; + } + + @Override + public List handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey, List currenttip) { + return currenttip; + } + + public static void layout(GuiContainer gui) { + VisiblityData visiblity = new VisiblityData(); + if (isHidden()) + visiblity.showNEI = false; + if (gui.height - gui.ySize <= 40) + visiblity.showSearchSection = false; + if (gui.guiLeft - 4 < 76) + visiblity.showWidgets = false; + + for (INEIGuiHandler handler : GuiInfo.guiHandlers) + handler.modifyVisiblity(gui, visiblity); + + visiblity.translateDependancies(); + + getLayoutStyle().layout(gui, visiblity); + + updateWidgetVisiblities(gui, visiblity); + } + + private static void init() { + itemPanel = new ItemPanel(); + dropDown = new Button("NEI Subsets") + { + @Override + public boolean onButtonPress(boolean rightclick) { + return false; + } + }; + searchField = new SearchField("search"); + + options = new Button("Options") + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (!rightclick) { + getOptionList().showGui(getGuiContainer()); + return true; + } + return false; + } + + @Override + public String getRenderLabel() { + return translate("inventory.options"); + } + }; + prev = new Button("Prev") + { + public boolean onButtonPress(boolean rightclick) { + if (!rightclick) { + LayoutManager.itemPanel.scroll(-1); + return true; + } + return false; + } + + @Override + public String getRenderLabel() { + return translate("inventory.prev"); + } + }; + next = new Button("Next") + { + public boolean onButtonPress(boolean rightclick) { + if (!rightclick) { + LayoutManager.itemPanel.scroll(1); + return true; + } + return false; + } + + @Override + public String getRenderLabel() { + return translate("inventory.next"); + } + }; + pageLabel = new Label("(0/0)", true); + more = new Button("+") + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (rightclick) + return false; + + int modifier = controlKey() ? 64 : shiftKey() ? 10 : 1; + + int quantity = getItemQuantity() + modifier; + if (quantity < 0) + quantity = 0; + + setItemQuantity(quantity); + return true; + } + }; + less = new Button("-") + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (rightclick) + return false; + + int modifier = controlKey() ? -64 : shiftKey() ? -10 : -1; + + int quantity = getItemQuantity() + modifier; + if (quantity < 0) + quantity = 0; + + setItemQuantity(quantity); + return true; + } + }; + quantity = new ItemQuantityField("quantity"); + + stateButtons = new SaveLoadButton[7]; + deleteButtons = new Button[7]; + + for (int i = 0; i < 7; i++) { + final int savestate = i; + stateButtons[i] = new SaveLoadButton("") + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (isStateSaved(savestate)) + loadState(savestate); + else + saveState(savestate); + return true; + } + + @Override + public void onTextChange() { + NBTTagCompound statelist = global.nbt.getCompoundTag("statename"); + global.nbt.setTag("statename", statelist); + + statelist.setString("" + savestate, label); + global.saveNBT(); + } + }; + deleteButtons[i] = new Button("x") + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (!rightclick) { + clearState(savestate); + return true; + } + return false; + } + }; + } + + delete = new Button() + { + @Override + public boolean onButtonPress(boolean rightclick) { + if ((state & 0x3) == 2) + return false; + + ItemStack held = getHeldItem(); + if (held != null) { + if (shiftKey()) { + deleteHeldItem(); + deleteItemsOfType(held); + } else if (rightclick) + decreaseSlotStack(-999); + else + deleteHeldItem(); + } else if (shiftKey()) + deleteEverything(); + else + NEIController.toggleDeleteMode(); + + return true; + } + + public String getButtonTip() { + if ((state & 0x3) != 2) { + if (shiftKey()) + return translate("inventory.delete.all"); + if (NEIController.canUseDeleteMode()) + return getStateTip("delete", state); + } + return null; + } + + @Override + public void postDraw(int mousex, int mousey) { + if(contains(mousex, mousey) && getHeldItem() != null && (state & 0x3) != 2) + GuiDraw.drawTip(mousex + 9, mousey, translate("inventory.delete." + (shiftKey() ? "all" : "one"), GuiContainerManager.itemDisplayNameShort(getHeldItem()))); + } + }; + gamemode = new ButtonCycled() + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (!rightclick) { + cycleGamemode(); + return true; + } + return false; + } + + public String getButtonTip() { + return translate("inventory.gamemode." + getNextGamemode()); + } + }; + gamemode.icons = new Image[3]; + rain = new Button() + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (handleDisabledButtonPress("rain", rightclick)) + return true; + + if (!rightclick) { + toggleRaining(); + return true; + } + return false; + } + + public String getButtonTip() { + return getStateTip("rain", state); + } + }; + magnet = new Button() + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (!rightclick) { + toggleMagnetMode(); + return true; + } + return false; + } + + public String getButtonTip() { + return getStateTip("magnet", state); + } + }; + for (int i = 0; i < 4; i++) { + final int zone = i; + timeButtons[i] = new Button() + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (handleDisabledButtonPress(NEIActions.timeZones[zone], rightclick)) + return true; + + if (!rightclick) { + setHourForward(zone * 6); + return true; + } + return false; + } + + @Override + public String getButtonTip() { + return getTimeTip(NEIActions.timeZones[zone], state); + } + + }; + } + heal = new Button() + { + @Override + public boolean onButtonPress(boolean rightclick) { + if (!rightclick) { + healPlayer(); + return true; + } + return false; + } + + @Override + public String getButtonTip() { + return translate("inventory.heal"); + } + }; + + delete.state |= 0x4; + gamemode.state |= 0x4; + rain.state |= 0x4; + magnet.state |= 0x4; + } + + private static String getStateTip(String name, int state) { + String sfx = (state & 0x3) == 2 ? "enable" : + (state & 0x3) == 1 ? "0" : "1"; + + return translate("inventory." + name + "." + sfx); + } + + private static String getTimeTip(String name, int state) { + String sfx = (state & 0x3) == 2 ? "enable" : "set"; + return translate("inventory." + name + "." + sfx); + } + + private static boolean handleDisabledButtonPress(String ident, boolean rightclick) { + if (!NEIActions.canDisable.contains(ident)) + return false; + if (rightclick != disabledActions.contains(ident)) + return setPropertyDisabled(ident, rightclick); + return false; + } + + private static boolean setPropertyDisabled(String ident, boolean disable) { + if (disable && NEIActions.base(ident).equals("time")) { + int count = 0; + for (int i = 0; i < 4; i++) { + if (disabledActions.contains(NEIActions.timeZones[i])) + count++; + } + if (count == 3) + return false; + } + if (hasSMPCounterPart()) + NEICPH.sendSetPropertyDisabled(ident, disable); + + return true; + } + + @Override + public void load(GuiContainer gui) { + if (isEnabled()) { + setInputFocused(null); + + ItemList.loadItems(); + overlayRenderer = null; + + getLayoutStyle().init(); + layout(gui); + } + + NEIController.load(gui); + + if (checkCreativeInv(gui)) { + if (gui.mc.currentScreen instanceof GuiContainerCreative)//makes cycle transititions seemless. Only closes if we are opening from fresh + gui.mc.displayGuiScreen(null); + return; + } + } + + @Override + public void refresh(GuiContainer gui) { + } + + public boolean checkCreativeInv(GuiContainer gui) { + if (gui instanceof GuiContainerCreative && invCreativeMode()) { + NEICPH.sendCreativeInv(true); + return true; + } else if (gui instanceof GuiExtendedCreativeInv && !invCreativeMode()) { + NEICPH.sendCreativeInv(false); + return true; + } + return false; + } + + public static void updateWidgetVisiblities(GuiContainer gui, VisiblityData visiblity) { + drawWidgets = new TreeSet(new WidgetZOrder(false)); + controlWidgets = new TreeSet(new WidgetZOrder(true)); + + if (!visiblity.showNEI) + return; + + addWidget(options); + if (visiblity.showItemPanel) { + addWidget(itemPanel); + addWidget(prev); + addWidget(next); + addWidget(pageLabel); + if (canPerformAction("item")) { + addWidget(more); + addWidget(less); + addWidget(quantity); + } + } + + if (visiblity.showSearchSection) { + addWidget(dropDown); + addWidget(searchField); + } + + if (canPerformAction("item") && visiblity.showStateButtons) { + for (int i = 0; i < 7; i++) { + addWidget(stateButtons[i]); + if (isStateSaved(i)) + addWidget(deleteButtons[i]); + } + } + if (visiblity.showUtilityButtons) { + if (canPerformAction("time")) { + for (int i = 0; i < 4; i++) + addWidget(timeButtons[i]); + } + if (canPerformAction("rain")) + addWidget(rain); + if (canPerformAction("heal")) + addWidget(heal); + if (canPerformAction("magnet")) + addWidget(magnet); + if (isValidGamemode("creative") || + isValidGamemode("creative+") || + isValidGamemode("adventure")) + addWidget(gamemode); + if (canPerformAction("delete")) + addWidget(delete); + } + } + + + public static LayoutStyle getLayoutStyle(int id) { + LayoutStyle style = layoutStyles.get(id); + if (style == null) + style = layoutStyles.get(0); + return style; + } + + public static LayoutStyle getLayoutStyle() { + return getLayoutStyle(NEIClientConfig.getLayoutStyle()); + } + + private static void addWidget(Widget widget) { + drawWidgets.add(widget); + controlWidgets.add(widget); + } + + @Override + public void guiTick(GuiContainer gui) { + if (checkCreativeInv(gui)) + return; + + if (!isEnabled()) + return; + + for (Widget widget : controlWidgets) + widget.update(); + } + + @Override + public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) { + if (isHidden() || !isEnabled()) + return false; + + for (Widget widget : controlWidgets) + if (widget.onMouseWheel(scrolled, mousex, mousey)) + return true; + + return false; + } + + @Override + public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) { + } + + @Override + public boolean shouldShowTooltip(GuiContainer gui) { + return itemPanel.draggedStack == null; + } + + public static Widget getInputFocused() { + return inputFocused; + } + + public static void setInputFocused(Widget widget) { + if (inputFocused != null) + inputFocused.loseFocus(); + + inputFocused = widget; + if (inputFocused != null) + inputFocused.gainFocus(); + } + + @Override + public void renderSlotUnderlay(GuiContainer gui, Slot slot) { + if (overlayRenderer != null) + overlayRenderer.renderOverlay(GuiContainerManager.getManager(gui), slot); + } + + @Override + public void renderSlotOverlay(GuiContainer window, Slot slot) { + ItemStack item = slot.getStack(); + if (world.nbt.getBoolean("searchinventories") && (item == null ? !getSearchExpression().equals("") : !ItemList.itemMatchesSearch(item))) { + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glTranslatef(0, 0, 150); + drawRect(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, 0x80000000); + GL11.glTranslatef(0, 0, -150); + GL11.glEnable(GL11.GL_LIGHTING); + } + } + + public static void drawIcon(int x, int y, Image image) { + changeTexture("nei:textures/nei_sprites.png"); + GL11.glColor4f(1, 1, 1, 1); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + drawTexturedModalRect(x, y, image.x, image.y, image.width, image.height); + GL11.glDisable(GL11.GL_BLEND); + } + + public static void drawButtonBackground(int x, int y, int w, int h, boolean edges, int type) { + int wtiles = 0; + int ew = w;//end width + if (w / 2 > 100) { + wtiles = (w - 200) / 50 + 1; + ew = 200; + } + + int w1 = ew / 2; + int h1 = h / 2; + int w2 = (ew + 1) / 2; + int h2 = (h + 1) / 2; + + int x2 = x + w - w2; + int y2 = y + h - h2; + + int ty = 46 + type * 20; + int te = (edges ? 0 : 1);//tex edges + + int ty1 = ty + te; + int tx1 = te; + int tx3 = 75; + //halfway the 1 is for odd number adjustment + int ty2 = ty + 20 - h2 - te; + int tx2 = 200 - w2 - te; + + changeTexture("textures/gui/widgets.png"); + drawTexturedModalRect(x, y, tx1, ty1, w1, h1);//top left + drawTexturedModalRect(x, y2, tx1, ty2, w1, h2);//bottom left + + for (int tile = 0; tile < wtiles; tile++) { + int tilex = x + w1 + 50 * tile; + drawTexturedModalRect(tilex, y, tx3, ty1, 50, h1);//top + drawTexturedModalRect(tilex, y2, tx3, ty2, 50, h2);//bottom + } + + drawTexturedModalRect(x2, y, tx2, ty1, w2, h1);//top right + drawTexturedModalRect(x2, y2, tx2, ty2, w2, h2);//bottom right + } + + public static LayoutManager instance() { + return instance; + } + + @Override + public void tickKeyStates() { + if (Minecraft.getMinecraft().currentScreen != null) + return; + + if (KeyManager.keyStates.get("world.dawn").down) + timeButtons[0].onButtonPress(false); + if (KeyManager.keyStates.get("world.noon").down) + timeButtons[1].onButtonPress(false); + if (KeyManager.keyStates.get("world.dusk").down) + timeButtons[2].onButtonPress(false); + if (KeyManager.keyStates.get("world.midnight").down) + timeButtons[3].onButtonPress(false); + if (KeyManager.keyStates.get("world.rain").down) + rain.onButtonPress(false); + if (KeyManager.keyStates.get("world.heal").down) + heal.onButtonPress(false); + if (KeyManager.keyStates.get("world.creative").down) + gamemode.onButtonPress(false); + } +} diff --git a/src/codechicken/nei/LayoutStyleDefault.java b/src/codechicken/nei/LayoutStyleDefault.java new file mode 100644 index 00000000..9fa995f1 --- /dev/null +++ b/src/codechicken/nei/LayoutStyleDefault.java @@ -0,0 +1,153 @@ +package codechicken.nei; + +import codechicken.nei.api.LayoutStyle; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.nbt.NBTTagCompound; + +import static codechicken.lib.gui.GuiDraw.getStringWidth; +import static codechicken.nei.LayoutManager.*; +import static codechicken.nei.NEIClientConfig.*; + +public abstract class LayoutStyleDefault extends LayoutStyle +{ + @Override + public void layout(GuiContainer gui, VisiblityData visiblity) { + int windowWidth = gui.width; + int windowHeight = gui.height; + int containerWidth = gui.xSize; + int containerLeft = gui.guiLeft; + + reset(); + + prev.y = 2; + prev.height = 16; + prev.width = containerLeft / 3; + prev.x = (containerWidth + windowWidth) / 2 + 2; + next.x = windowWidth - prev.width - 2; + + next.y = prev.y; + next.width = prev.width; + next.height = prev.height; + pageLabel.x = containerLeft * 3 / 2 + containerWidth + 1; + pageLabel.y = prev.y + 5; + pageLabel.text = "(" + itemPanel.getPage() + "/" + itemPanel.getNumPages() + ")"; + + itemPanel.y = prev.height + prev.y; + itemPanel.x = (containerWidth + windowWidth) / 2 + 3; + itemPanel.width = windowWidth - 3 - itemPanel.x; + itemPanel.height = windowHeight - 15 - itemPanel.y; + if (!canPerformAction("item")) + itemPanel.height += 15; + itemPanel.resize(); + + more.width = more.height = less.width = less.height = 16; + less.x = prev.x; + more.x = windowWidth - less.width - 2; + more.y = less.y = windowHeight - more.height - 2; + + quantity.x = less.x + less.width + 2; + quantity.y = less.y; + quantity.width = more.x - quantity.x - 2; + quantity.height = less.height; + + options.x = isEnabled() ? 0 : 6; + options.y = isEnabled() ? windowHeight - 22 : windowHeight - 28; + options.width = 80; + options.height = 22; + + delete.state = 0x4; + if (NEIController.getDeleteMode()) + delete.state |= 1; + else if (!visiblity.enableDeleteMode) + delete.state |= 2; + + rain.state = 0x4; + if (disabledActions.contains("rain")) + rain.state |= 2; + else if (NEIClientUtils.isRaining()) + rain.state |= 1; + + gamemode.state = 0x4; + if (NEIClientUtils.getGamemode() != 0) { + gamemode.state |= 0x1; + gamemode.index = NEIClientUtils.getGamemode() - 1; + } else { + if (NEIClientUtils.isValidGamemode("creative")) + gamemode.index = 0; + else if (NEIClientUtils.isValidGamemode("creative+")) + gamemode.index = 1; + else if (NEIClientUtils.isValidGamemode("adventure")) + gamemode.index = 2; + } + + magnet.state = 0x4 | (getMagnetMode() ? 1 : 0); + + if (canPerformAction("delete")) + layoutButton(delete); + if (canPerformAction("rain")) + layoutButton(rain); + if (NEIClientUtils.isValidGamemode("creative") || + NEIClientUtils.isValidGamemode("creative+") || + NEIClientUtils.isValidGamemode("adventure")) + layoutButton(gamemode); + if (canPerformAction("magnet")) + layoutButton(magnet); + if (canPerformAction("time")) { + for (int i = 0; i < 4; i++) { + timeButtons[i].state = disabledActions.contains(NEIActions.timeZones[i]) ? 2 : 0; + layoutButton(timeButtons[i]); + } + } + if (canPerformAction("heal")) + layoutButton(heal); + + searchField.y = windowHeight - searchField.height - 2; + + dropDown.height = 20; + dropDown.width = prev.x - dropDown.x - 3; + searchField.height = 20; + searchField.width = 150; + searchField.x = (windowWidth - searchField.width) / 2; + + if (!visiblity.showItemSection) { + //TODO dropDown.setDropDown(0); + searchField.setFocus(false); + } + + int maxWidth = 0; + for (int i = 0; i < 7; i++) { + deleteButtons[i].width = 16; + deleteButtons[i].height = 16; + + NBTTagCompound statelist = global.nbt.getCompoundTag("statename"); + global.nbt.setTag("statename", statelist); + String name = statelist.getString("" + i); + if (statelist.getTag("" + i) == null) { + name = "" + (i + 1); + statelist.setString("" + i, name); + } + stateButtons[i].label = name; + stateButtons[i].saved = isStateSaved(i); + + int width = getStringWidth(stateButtons[i].getRenderLabel()) + 26; + if (width + 22 > containerLeft) + width = containerLeft - 22; + + if (width > maxWidth) + maxWidth = width; + } + + for (int i = 0; i < 7; i++) { + stateButtons[i].x = 0; + stateButtons[i].y = 58 + i * 22; + stateButtons[i].height = 20; + + stateButtons[i].x = 0; + stateButtons[i].width = maxWidth; + deleteButtons[i].x = stateButtons[i].width + 3; + deleteButtons[i].y = stateButtons[i].y + 2; + } + } + + public abstract void layoutButton(Button button); +} diff --git a/src/codechicken/nei/LayoutStyleMinecraft.java b/src/codechicken/nei/LayoutStyleMinecraft.java new file mode 100644 index 00000000..22eb5625 --- /dev/null +++ b/src/codechicken/nei/LayoutStyleMinecraft.java @@ -0,0 +1,89 @@ +package codechicken.nei; + +import org.lwjgl.opengl.GL11; + +import static codechicken.lib.gui.GuiDraw.drawStringC; +import static codechicken.nei.LayoutManager.*; + +public class LayoutStyleMinecraft extends LayoutStyleDefault +{ + int stateButtonCount; + int clickButtonCount; + + @Override + public String getName() { + return "minecraft"; + } + + @Override + public void init() { + delete.icon = new Image(144, 12, 12, 12); + gamemode.icons[0] = new Image(132, 12, 12, 12); + gamemode.icons[1] = new Image(156, 12, 12, 12); + gamemode.icons[2] = new Image(168, 12, 12, 12); + rain.icon = new Image(120, 12, 12, 12); + magnet.icon = new Image(180, 24, 12, 12); + timeButtons[0].icon = new Image(132, 24, 12, 12); + timeButtons[1].icon = new Image(120, 24, 12, 12); + timeButtons[2].icon = new Image(144, 24, 12, 12); + timeButtons[3].icon = new Image(156, 24, 12, 12); + heal.icon = new Image(168, 24, 12, 12); + dropDown.x = 90; + } + + @Override + public void reset() { + stateButtonCount = clickButtonCount = 0; + } + + @Override + public void layoutButton(Button button) { + if ((button.state & 0x4) != 0) { + button.x = 6 + stateButtonCount * 20; + button.y = 3; + stateButtonCount++; + } else { + button.x = 6 + (clickButtonCount % 4) * 20; + button.y = 3 + (1 + clickButtonCount / 4) * 18; + clickButtonCount++; + } + + button.height = 17; + button.width = button.contentWidth() + 6; + } + + @Override + public void drawButton(Button b, int mousex, int mousey) { + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glColor4f(1, 1, 1, 1); + + int tex; + if ((b.state & 0x3) == 2) + tex = 0; + else if ((b.state & 0x4) == 0 && b.contains(mousex, mousey) ||//not a state button and mouseover + (b.state & 0x3) == 1)//state active + tex = 2; + else + tex = 1; + LayoutManager.drawButtonBackground(b.x, b.y, b.width, b.height, true, tex); + + Image icon = b.getRenderIcon(); + if (icon == null) { + int colour = tex == 2 ? 0xffffa0 : + tex == 0 ? 0x601010 : 0xe0e0e0; + + drawStringC(b.getRenderLabel(), b.x + b.width / 2, b.y + (b.height - 8) / 2, colour); + } else { + GL11.glColor4f(1, 1, 1, 1); + + int iconx = b.x + (b.width - icon.width) / 2; + int icony = b.y + (b.height - icon.height) / 2; + LayoutManager.drawIcon(iconx, icony, icon); + } + } + + @Override + public boolean texturedButtons() { + return true; + } +} diff --git a/src/codechicken/nei/LayoutStyleTMIOld.java b/src/codechicken/nei/LayoutStyleTMIOld.java new file mode 100644 index 00000000..3b748a8e --- /dev/null +++ b/src/codechicken/nei/LayoutStyleTMIOld.java @@ -0,0 +1,127 @@ +package codechicken.nei; + +import codechicken.nei.guihook.GuiContainerManager; + +import static codechicken.lib.gui.GuiDraw.drawRect; +import static codechicken.lib.gui.GuiDraw.drawString; +import static codechicken.nei.LayoutManager.*; + +public class LayoutStyleTMIOld extends LayoutStyleDefault +{ + public static final Image stateOff = new Image(48, 0, 8, 12); + public static final Image stateOn = new Image(56, 0, 8, 12); + public static final Image stateDisabled = new Image(64, 0, 8, 12); + + int stateButtonCount; + int clickButtonCount; + + @Override + public String getName() + { + return "oldtmi"; + } + + @Override + public void init() + { + delete.icon = new Image(24, 12, 12, 12); + gamemode.icons[0] = new Image(12, 12, 12, 12); + gamemode.icons[1] = new Image(36, 12, 12, 12); + gamemode.icons[2] = new Image(48, 12, 12, 12); + rain.icon = new Image(0, 12, 12, 12); + magnet.icon = new Image(60, 24, 12, 12); + timeButtons[0].icon = new Image(12, 24, 12, 12); + timeButtons[1].icon = new Image(0, 24, 12, 12); + timeButtons[2].icon = new Image(24, 24, 12, 12); + timeButtons[3].icon = new Image(36, 24, 12, 12); + heal.icon = new Image(48, 24, 12, 12); + dropDown.x = 93; + } + + @Override + public void reset() + { + stateButtonCount = clickButtonCount = 0; + } + + @Override + public void layoutButton(Button button) + { + int offsetx = 2; + int offsety = 2; + + if((button.state & 0x4) != 0) + { + button.x = offsetx + stateButtonCount*22; + button.y = offsety; + stateButtonCount++; + } + else + { + button.x = offsetx + (clickButtonCount%4)*22; + button.y = offsety + (1+clickButtonCount/4)*17; + clickButtonCount++; + } + + button.height = 14; + button.width = button.contentWidth() + 2; + if((button.state & 0x4) != 0) + button.width += stateOff.width; + } + + @Override + public void drawBackground(GuiContainerManager gui) + { + if(clickButtonCount == 0 && stateButtonCount == 0) + return; + + int maxx = Math.max(stateButtonCount, clickButtonCount); + if(maxx > 4)maxx = 4; + int maxy = clickButtonCount == 0 ? 1 : (clickButtonCount / 4 + 2); + + drawRect(0, 0, 2+22*maxx, 1+maxy*17, 0xFF000000); + } + + @Override + public void drawButton(Button b, int mousex, int mousey) + { + int cwidth = b.contentWidth(); + if((b.state & 0x4) != 0) + cwidth += stateOff.width; + int textx = b.x + (b.width - cwidth) / 2; + int texty = b.y + (b.height - 8) / 2; + + drawRect(b.x, b.y, b.width, b.height, b.contains(mousex, mousey) ? 0xee401008 : 0xee000000); + + Image icon = b.getRenderIcon(); + if(icon == null) + { + drawString(b.getRenderLabel(), textx, texty, -1); + } + else + { + int icony = b.y + (b.height - icon.height) / 2; + LayoutManager.drawIcon(textx, icony, icon); + if((b.state & 0x3) == 2) + drawRect(textx, icony, icon.width, icon.height, 0x80000000); + + if((b.state & 0x4) != 0) + { + Image stateimage; + if((b.state & 0x3) == 1) + stateimage = stateOn; + else if((b.state & 0x3) == 2) + stateimage = stateDisabled; + else + stateimage = stateOff; + LayoutManager.drawIcon(textx+icon.width, icony, stateimage); + } + } + } + + @Override + public boolean texturedButtons() + { + return false; + } +} diff --git a/src/codechicken/nei/NEIActions.java b/src/codechicken/nei/NEIActions.java new file mode 100644 index 00000000..76371ce4 --- /dev/null +++ b/src/codechicken/nei/NEIActions.java @@ -0,0 +1,95 @@ +package codechicken.nei; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +public class NEIActions +{ + public static final int protocol = 0; + + public static HashMap nameActionMap = new HashMap(); + public static HashSet canDisable = new HashSet(); + + public static void addAction(String name, String base, boolean smpreq) + { + NEIActions action = new NEIActions(name, base, smpreq); + nameActionMap.put(name, action); + } + + private static void addAction(String name, String base) + { + addAction(name, base, false); + } + + public static void addAction(String name) + { + addAction(name, name); + } + + private static void addAction(String name, boolean smpreq) + { + addAction(name, name, smpreq); + } + + public static String base(String name) + { + NEIActions action = nameActionMap.get(name); + return action == null ? name : action.base; + } + + public static boolean smpRequired(String name) + { + return nameActionMap.get(name).smpreq; + } + + public static void init() + { + addAction("time"); + addAction("dawn", "time"); + addAction("noon", "time"); + addAction("dusk", "time"); + addAction("midnight", "time"); + addAction("creative"); + addAction("creative+", true); + addAction("adventure", "creative"); + addAction("rain"); + addAction("item"); + addAction("heal"); + addAction("delete", true); + addAction("magnet", true); + addAction("enchant", true); + addAction("potion", true); + addAction("itemnbt", "item", true); + + canDisable.add("dawn"); + canDisable.add("noon"); + canDisable.add("dusk"); + canDisable.add("midnight"); + canDisable.add("rain"); + } + + public static final String[] timeZones = new String[]{"dawn", "noon", "dusk", "midnight"}; + public static final String[] gameModes = new String[]{"survival", "creative", "creative+", "adventure"}; + + public String name; + public String base; + public boolean smpreq; + + public NEIActions(String name, String base, boolean smpreq) + { + this.name = name; + this.base = base; + this.smpreq = smpreq; + } + + public static List baseActions() + { + List list = new LinkedList(); + for(NEIActions a : nameActionMap.values()) + if(a.base.equals(a.name)) + list.add(a.name); + return list; + } +} diff --git a/src/codechicken/nei/NEICPH.java b/src/codechicken/nei/NEICPH.java new file mode 100644 index 00000000..f16cc8a1 --- /dev/null +++ b/src/codechicken/nei/NEICPH.java @@ -0,0 +1,249 @@ +package codechicken.nei; + +import codechicken.core.ClientUtils; +import codechicken.lib.inventory.InventoryUtils; +import codechicken.lib.packet.PacketCustom; +import codechicken.lib.packet.PacketCustom.IClientPacketHandler; +import cpw.mods.fml.relauncher.Side; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.network.play.INetHandlerPlayClient; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.world.World; + +public class NEICPH implements IClientPacketHandler +{ + public static final String channel = "NEI"; + + @Override + public void handlePacket(PacketCustom packet, Minecraft mc, INetHandlerPlayClient netHandler) { + switch (packet.getType()) { + case 1: + handleSMPCheck(packet.readUByte(), packet.readString(), mc.theWorld); + break; + case 10: + handleLoginState(packet); + break; + case 11: + handleActionDisabled(packet); + break; + case 12: + handleActionEnabled(packet); + break; + case 13: + ClientHandler.instance().addSMPMagneticItem(packet.readInt(), mc.theWorld); + break; + case 14: + handleGamemode(mc, packet.readUByte()); + break; + case 21: + ClientUtils.openSMPGui(packet.readUByte(), new GuiEnchantmentModifier(mc.thePlayer.inventory, mc.theWorld, 0, 0, 0)); + break; + case 23: + if (packet.readBoolean()) + ClientUtils.openSMPGui(packet.readUByte(), new GuiExtendedCreativeInv(new ContainerCreativeInv(mc.thePlayer, new ExtendedCreativeInv(null, Side.CLIENT)))); + else + mc.displayGuiScreen(new GuiInventory(mc.thePlayer)); + break; + case 24: + ClientUtils.openSMPGui(packet.readUByte(), new GuiPotionCreator(mc.thePlayer.inventory)); + break; + } + } + + private void handleGamemode(Minecraft mc, int mode) { + mc.playerController.setGameType(NEIServerUtils.getGameType(mode)); + } + + private void handleActionEnabled(PacketCustom packet) { + String name = packet.readString(); + if (packet.readBoolean()) + NEIClientConfig.enabledActions.add(name); + else + NEIClientConfig.enabledActions.remove(name); + } + + private void handleActionDisabled(PacketCustom packet) { + String name = packet.readString(); + if (packet.readBoolean()) + NEIClientConfig.disabledActions.add(name); + else + NEIClientConfig.disabledActions.remove(name); + } + + private void handleLoginState(PacketCustom packet) { + NEIClientConfig.permissableActions.clear(); + int num = packet.readUByte(); + for (int i = 0; i < num; i++) + NEIClientConfig.permissableActions.add(packet.readString()); + + NEIClientConfig.disabledActions.clear(); + num = packet.readUByte(); + for (int i = 0; i < num; i++) + NEIClientConfig.disabledActions.add(packet.readString()); + + NEIClientConfig.enabledActions.clear(); + num = packet.readUByte(); + for (int i = 0; i < num; i++) + NEIClientConfig.enabledActions.add(packet.readString()); + + NEIClientConfig.bannedBlocks.clear(); + num = packet.readInt(); + for(int i = 0; i < num; i++) + NEIClientConfig.bannedBlocks.add(packet.readItemStack()); + + if (NEIClientUtils.getGuiContainer() != null) + LayoutManager.instance().refresh(NEIClientUtils.getGuiContainer()); + } + + private void handleSMPCheck(int serverprotocol, String worldName, World world) { + if (serverprotocol > NEIActions.protocol) { + NEIClientUtils.printChatMessage(new ChatComponentTranslation("nei.chat.mismatch.client")); + } else if (serverprotocol < NEIActions.protocol) { + NEIClientUtils.printChatMessage(new ChatComponentTranslation("nei.chat.mismatch.server")); + } else { + try { + ClientHandler.instance().loadWorld(world, true); + NEIClientConfig.loadWorld(getSaveName(worldName)); + NEIClientConfig.setHasSMPCounterPart(true); + sendRequestLoginInfo(); + } catch (Exception nce) { + nce.printStackTrace(); + } + } + } + + private static String getSaveName(String worldName) { + if (Minecraft.getMinecraft().isSingleplayer()) + return "local/" + ClientUtils.getWorldSaveName(); + + return "remote/" + ClientUtils.getServerIP().replace(':', '~') + "/" + worldName; + } + + public static void sendGiveItem(ItemStack spawnstack, boolean infinite, boolean doSpawn) { + PacketCustom packet = new PacketCustom(channel, 1); + packet.writeItemStack(spawnstack, true); + packet.writeBoolean(infinite); + packet.writeBoolean(doSpawn); + packet.sendToServer(); + } + + public static void sendDeleteAllItems() { + PacketCustom packet = new PacketCustom(channel, 4); + packet.sendToServer(); + } + + public static void sendStateLoad(ItemStack[] state) { + sendDeleteAllItems(); + for (int slot = 0; slot < state.length; slot++) { + ItemStack item = state[slot]; + if (item == null) { + continue; + } + sendSetSlot(slot, item, false); + } + + PacketCustom packet = new PacketCustom(channel, 11); + packet.sendToServer(); + } + + public static void sendSetSlot(int slot, ItemStack stack, boolean container) { + PacketCustom packet = new PacketCustom(channel, 5); + packet.writeBoolean(container); + packet.writeShort(slot); + packet.writeItemStack(stack); + packet.sendToServer(); + } + + private static void sendRequestLoginInfo() { + PacketCustom packet = new PacketCustom(channel, 10); + packet.sendToServer(); + } + + public static void sendToggleMagnetMode() { + PacketCustom packet = new PacketCustom(channel, 6); + packet.sendToServer(); + } + + public static void sendSetTime(int hour) { + PacketCustom packet = new PacketCustom(channel, 7); + packet.writeByte(hour); + packet.sendToServer(); + } + + public static void sendHeal() { + PacketCustom packet = new PacketCustom(channel, 8); + packet.sendToServer(); + } + + public static void sendToggleRain() { + PacketCustom packet = new PacketCustom(channel, 9); + packet.sendToServer(); + } + + public static void sendOpenEnchantmentWindow() { + PacketCustom packet = new PacketCustom(channel, 21); + packet.sendToServer(); + } + + public static void sendModifyEnchantment(int enchID, int level, boolean add) { + PacketCustom packet = new PacketCustom(channel, 22); + packet.writeByte(enchID); + packet.writeByte(level); + packet.writeBoolean(add); + packet.sendToServer(); + } + + public static void sendSetPropertyDisabled(String name, boolean enable) { + PacketCustom packet = new PacketCustom(channel, 12); + packet.writeString(name); + packet.writeBoolean(enable); + packet.sendToServer(); + } + + public static void sendGamemode(int mode) { + new PacketCustom(channel, 13) + .writeByte(mode) + .sendToServer(); + } + + public static void sendCreativeInv(boolean open) { + PacketCustom packet = new PacketCustom(channel, 23); + packet.writeBoolean(open); + packet.sendToServer(); + } + + public static void sendCreativeScroll(int steps) { + PacketCustom packet = new PacketCustom(channel, 14); + packet.writeInt(steps); + packet.sendToServer(); + } + + public static void sendMobSpawnerID(int x, int y, int z, String mobtype) { + PacketCustom packet = new PacketCustom(channel, 15); + packet.writeCoord(x, y, z); + packet.writeString(mobtype); + packet.sendToServer(); + } + + public static PacketCustom createContainerPacket() { + return new PacketCustom(channel, 20); + } + + public static void sendOpenPotionWindow() { + ItemStack[] potionStore = new ItemStack[9]; + InventoryUtils.readItemStacksFromTag(potionStore, NEIClientConfig.global.nbt.getCompoundTag("potionStore").getTagList("items", 10)); + PacketCustom packet = new PacketCustom(channel, 24); + for (ItemStack stack : potionStore) + packet.writeItemStack(stack); + packet.sendToServer(); + } + + public static void sendDummySlotSet(int slotNumber, ItemStack stack) { + PacketCustom packet = new PacketCustom(channel, 25); + packet.writeShort(slotNumber); + packet.writeItemStack(stack, true); + packet.sendToServer(); + } +} diff --git a/src/codechicken/nei/NEIChestGuiHandler.java b/src/codechicken/nei/NEIChestGuiHandler.java new file mode 100644 index 00000000..98d30eff --- /dev/null +++ b/src/codechicken/nei/NEIChestGuiHandler.java @@ -0,0 +1,29 @@ +package codechicken.nei; + +import codechicken.nei.api.INEIGuiAdapter; +import codechicken.nei.api.TaggedInventoryArea; +import net.minecraft.client.gui.inventory.GuiChest; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.ContainerChest; +import net.minecraft.item.ItemStack; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class NEIChestGuiHandler extends INEIGuiAdapter +{ + public int chestSize(GuiContainer gui) { + return ((ContainerChest) gui.inventorySlots).getLowerChestInventory().getSizeInventory(); + } + + @Override + public Iterable getItemSpawnSlots(GuiContainer gui, ItemStack item) { + return gui instanceof GuiChest ? NEIServerUtils.getRange(0, chestSize(gui)) : Collections.emptyList(); + } + + @Override + public List getInventoryAreas(GuiContainer gui) { + return gui instanceof GuiChest ? Arrays.asList(new TaggedInventoryArea("Chest", 0, chestSize(gui), gui.inventorySlots)) : null; + } +} diff --git a/src/codechicken/nei/NEIClientConfig.java b/src/codechicken/nei/NEIClientConfig.java new file mode 100644 index 00000000..98ff85c9 --- /dev/null +++ b/src/codechicken/nei/NEIClientConfig.java @@ -0,0 +1,527 @@ +package codechicken.nei; + +import codechicken.core.*; +import codechicken.lib.config.ConfigFile; +import codechicken.lib.config.ConfigTag; +import codechicken.lib.config.ConfigTagParent; +import codechicken.nei.api.*; +import codechicken.nei.config.*; +import codechicken.nei.recipe.RecipeInfo; +import codechicken.obfuscator.ObfuscationRun; +import net.minecraft.client.AnvilConverterException; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.world.World; +import net.minecraft.world.storage.SaveFormatComparator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.input.Keyboard; + +import java.io.File; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; + +public class NEIClientConfig +{ + private static boolean configLoaded; + private static boolean enabledOverride; + + public static Logger logger = LogManager.getLogger("NotEnoughItems"); + public static File configDir = new File(CommonUtils.getMinecraftDir(), "config/NEI/"); + public static ConfigSet global = new ConfigSet( + new File("saves/NEI/client.dat"), + new ConfigFile(new File(configDir, "client.cfg"))); + public static ConfigSet world; + + //public static ItemVisibilityHash vishash; + + public static ItemStack creativeInv[]; + private static boolean statesSaved[] = new boolean[7]; + + public static boolean hasSMPCounterpart; + public static HashSet permissableActions = new HashSet(); + public static HashSet disabledActions = new HashSet(); + public static HashSet enabledActions = new HashSet(); + + public static ItemStackSet bannedBlocks = new ItemStackSet(); + + static { + if (global.config.getTag("checkUpdates").getBooleanValue(true)) + CCUpdateChecker.updateCheck("NotEnoughItems"); + linkOptionList(); + setDefaults(); + } + + private static void setDefaults() { + ConfigTagParent tag = global.config; + tag.setComment("Main configuration of NEI.\nMost of these options can be changed ingame.\nDeleting any element will restore it to it's default value"); + + tag.getTag("command").useBraces().setComment("Change these options if you have a different mod installed on the server that handles the commands differently, Eg. Bukkit Essentials"); + tag.setNewLineMode(1); + + tag.getTag("inventory.widgetsenabled").getBooleanValue(true); + API.addOption(new OptionToggleButton("inventory.widgetsenabled")); + + tag.getTag("inventory.hidden").getBooleanValue(false); + tag.getTag("inventory.cheatmode").getIntValue(2); + tag.getTag("inventory.lockmode").setComment("For those who can't help themselves.\nSet this to a mode and you will be unable to change it ingame").getIntValue(-1); + API.addOption(new OptionCycled("inventory.cheatmode", 3) + { + @Override + public boolean optionValid(int index) { + return getLockedMode() == -1 || getLockedMode() == index && NEIInfo.isValidMode(index); + } + }); + checkCheatMode(); + + tag.getTag("inventory.utilities").setDefaultValue("delete, magnet"); + API.addOption(new OptionUtilities("inventory.utilities")); + + tag.getTag("inventory.gamemodes").setDefaultValue("creative, creative+, adventure"); + API.addOption(new OptionGamemodes("inventory.gamemodes")); + + tag.getTag("inventory.layoutstyle").getIntValue(0); + API.addOption(new OptionCycled("inventory.layoutstyle", 0) + { + @Override + public String getPrefix() { + return translateN(name); + } + + @Override + public String getButtonText() { + return NEIClientUtils.translate("layoutstyle." + + LayoutManager.getLayoutStyle(renderTag().getIntValue()).getName()); + } + + @Override + public boolean cycle() { + LinkedList list = new LinkedList(); + for (Entry entry : LayoutManager.layoutStyles.entrySet()) + list.add(entry.getKey()); + + Collections.sort(list); + + int nextLayout = getTag().getIntValue(); + if (nextLayout == list.getLast())//loop list + nextLayout = -1; + for (Integer i : list) { + if (i > nextLayout) { + nextLayout = i; + break; + } + } + + getTag().setIntValue(nextLayout); + return true; + } + }); + tag.getTag("inventory.itemIDs").getIntValue(1); + API.addOption(new OptionCycled("inventory.itemIDs", 3, true)); + + tag.getTag("world.highlight_tips").getBooleanValue(false); + tag.getTag("world.highlight_tips.x").getIntValue(5000); + tag.getTag("world.highlight_tips.y").getIntValue(100); + API.addOption(new OptionHighlightTips("world.highlight_tips")); + + tag.getTag("inventory.profileRecipes").getBooleanValue(false); + API.addOption(new OptionToggleButton("inventory.profileRecipes", true)); + + tag.getTag("command.creative").setDefaultValue("/gamemode {0} {1}"); + API.addOption(new OptionTextField("command.creative")); + tag.getTag("command.item").setDefaultValue("/give {0} {1} {2} {3}"); + API.addOption(new OptionTextField("command.item")); + tag.getTag("command.time").setDefaultValue("/time set {0}"); + API.addOption(new OptionTextField("command.time")); + tag.getTag("command.rain").setDefaultValue("/toggledownfall"); + API.addOption(new OptionTextField("command.rain")); + tag.getTag("command.heal").setDefaultValue(""); + API.addOption(new OptionTextField("command.heal")); + + setDefaultKeyBindings(); + } + + private static void linkOptionList() { + getOptionList().bindConfig(new IConfigSetHolder() + { + @Override + public ConfigSet worldConfigSet() { + return world; + } + + @Override + public ConfigSet globalConfigSet() { + return global; + } + }); + } + + private static void setDefaultKeyBindings() { + API.addKeyBind("gui.recipe", Keyboard.KEY_R); + API.addKeyBind("gui.usage", Keyboard.KEY_U); + API.addKeyBind("gui.back", Keyboard.KEY_BACK); + API.addKeyBind("gui.enchant", Keyboard.KEY_X); + API.addKeyBind("gui.potion", Keyboard.KEY_P); + API.addKeyBind("gui.prev", Keyboard.KEY_PRIOR); + API.addKeyBind("gui.next", Keyboard.KEY_NEXT); + API.addKeyBind("gui.hide", Keyboard.KEY_O); + API.addKeyBind("gui.search", Keyboard.KEY_F); + API.addKeyBind("world.chunkoverlay", Keyboard.KEY_F9); + API.addKeyBind("world.moboverlay", Keyboard.KEY_F7); + API.addKeyBind("world.highlight_tips", Keyboard.KEY_NUMPAD0); + API.addKeyBind("world.dawn", 0); + API.addKeyBind("world.noon", 0); + API.addKeyBind("world.dusk", 0); + API.addKeyBind("world.midnight", 0); + API.addKeyBind("world.rain", 0); + API.addKeyBind("world.heal", 0); + API.addKeyBind("world.creative", 0); + } + + public static OptionList getOptionList() { + return OptionList.getOptionList("nei.options"); + } + + public static void loadWorld(String saveName) { + setInternalEnabled(true); + logger.debug("Loading "+(Minecraft.getMinecraft().isSingleplayer() ? "Local" : "Remote")+" World"); + bootNEI(ClientUtils.getWorld()); + + File saveDir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + saveName); + boolean newWorld = !saveDir.exists(); + if (newWorld) + saveDir.mkdirs(); + + world = new ConfigSet(new File(saveDir, "NEI.dat"), new ConfigFile(new File(saveDir, "NEI.cfg"))); + onWorldLoad(newWorld); + } + + private static void onWorldLoad(boolean newWorld) { + world.config.setComment("World based configuration of NEI.\nMost of these options can be changed ingame.\nDeleting any element will restore it to it's default value"); + + setWorldDefaults(); + creativeInv = new ItemStack[54]; + LayoutManager.searchField.setText(getSearchExpression()); + LayoutManager.quantity.setText(Integer.toString(getItemQuantity())); + + if (newWorld && Minecraft.getMinecraft().isSingleplayer()) + world.config.getTag("inventory.cheatmode").setIntValue(NEIClientUtils.getGamemode() == 1 ? 2 : 0); + + NEIInfo.load(ClientUtils.getWorld()); + } + + private static void setWorldDefaults() { + NBTTagCompound nbt = world.nbt; + if (!nbt.hasKey("search")) nbt.setString("search", ""); + if (!nbt.hasKey("quantity")) nbt.setInteger("quantity", 0); + if (!nbt.hasKey("validateenchantments")) nbt.setBoolean("validateenchantments", false); + + world.saveNBT(); + } + + public static int getKeyBinding(String string) { + return getSetting("keys." + string).getIntValue(); + } + + public static void setDefaultKeyBinding(String string, int key) { + getSetting("keys." + string).getIntValue(key); + } + + public static void bootNEI(World world) { + if (configLoaded) + return; + + loadStates(); + //ItemVisibilityHash.loadStates(); + //vishash = new ItemVisibilityHash(); + ItemInfo.load(world); + GuiInfo.load(); + RecipeInfo.load(); + LayoutManager.load(); + NEIController.load(); + + configLoaded = true; + + ClassDiscoverer classDiscoverer = new ClassDiscoverer(new IStringMatcher() + { + public boolean matches(String test) { + return test.startsWith("NEI") && test.endsWith("Config.class"); + } + }, IConfigureNEI.class); + + classDiscoverer.findClasses(); + + for (Class class1 : classDiscoverer.classes) { + try { + IConfigureNEI config = (IConfigureNEI) class1.newInstance(); + config.loadConfig(); + NEIModContainer.plugins.add(config); + logger.debug("Loaded " + class1.getName()); + } catch (Exception e) { + logger.error("Failed to Load " + class1.getName()); + e.printStackTrace(); + } + } + } + + public static void loadStates() { + for (int state = 0; state < 7; state++) + statesSaved[state] = !global.nbt.getCompoundTag("save" + state).hasNoTags(); + } + + public static boolean isWorldSpecific(String setting) { + return world != null && world.config.containsTag(setting); + } + + public static boolean isStateSaved(int i) { + return statesSaved[i]; + } + + public static ConfigTag getSetting(String s) { + return isWorldSpecific(s) ? world.config.getTag(s) : global.config.getTag(s); + } + + public static boolean getBooleanSetting(String s) { + return getSetting(s).getBooleanValue(); + } + + public static boolean isHidden() { + return !enabledOverride || getBooleanSetting("inventory.hidden"); + } + + public static boolean isEnabled() { + return enabledOverride && getBooleanSetting("inventory.widgetsenabled"); + } + + public static void setEnabled(boolean flag) { + getSetting("inventory.widgetsenabled").setBooleanValue(flag); + } + + public static int getItemQuantity() { + return world.nbt.getInteger("quantity"); + } + + public static int getCheatMode() { + return getIntSetting("inventory.cheatmode"); + } + + private static void checkCheatMode() { + if (getLockedMode() != -1) + setIntSetting("inventory.cheatmode", getLockedMode()); + } + + public static int getLockedMode() { + return getIntSetting("inventory.lockmode"); + } + + public static int getLayoutStyle() { + return getIntSetting("inventory.layoutstyle"); + } + + public static String getStringSetting(String s) { + return getSetting(s).getValue(); + } + + public static boolean canDump() { + return getBooleanSetting("ID dump.itemIDs") || getBooleanSetting("ID dump.blockIDs") || getBooleanSetting("ID dump.unused itemIDs") || getBooleanSetting("ID dump.unused blockIDs"); + } + + public static boolean showIDs() { + int i = getIntSetting("inventory.itemIDs"); + return i == 2 || (i == 1 && isEnabled() && !isHidden()); + } + + public static void toggleBooleanSetting(String setting) { + ConfigTag tag = getSetting(setting); + tag.setBooleanValue(!tag.getBooleanValue()); + } + + public static void cycleSetting(String setting, int max) { + ConfigTag tag = getSetting(setting); + tag.setIntValue((tag.getIntValue() + 1) % max); + } + + public static int getIntSetting(String setting) { + return getSetting(setting).getIntValue(); + } + + public static void setIntSetting(String setting, int val) { + getSetting(setting).setIntValue(val); + } + + public static String getSearchExpression() { + return world.nbt.getString("search"); + } + + public static void setSearchExpression(String expression) { + world.nbt.setString("search", expression); + world.saveNBT(); + } + + public static boolean getMagnetMode() { + return enabledActions.contains("magnet"); + } + + public static boolean invCreativeMode() { + return enabledActions.contains("creative+") && canPerformAction("creative+"); + } + + public static boolean areDamageVariantsShown() { + return hasSMPCounterPart() || getSetting("command.item").getValue().contains("{3}"); + } + + public static void clearState(int state) { + statesSaved[state] = false; + global.nbt.setTag("save" + state, new NBTTagCompound()); + global.saveNBT(); + } + + public static void loadState(int state) { + if (!statesSaved[state]) { + return; + } + + NBTTagCompound statesave = global.nbt.getCompoundTag("save" + state); + GuiContainer currentContainer = NEIClientUtils.getGuiContainer(); + LinkedList saveAreas = new LinkedList(); + saveAreas.add(new TaggedInventoryArea(NEIClientUtils.mc().thePlayer.inventory)); + + for (INEIGuiHandler handler : GuiInfo.guiHandlers) { + List areaList = handler.getInventoryAreas(currentContainer); + if (areaList != null) + saveAreas.addAll(areaList); + } + + for (TaggedInventoryArea area : saveAreas) { + if (!statesave.hasKey(area.tagName)) + continue; + + for (int slot : area.slots) { + NEIClientUtils.setSlotContents(slot, null, area.isContainer()); + } + + NBTTagList areaTag = statesave.getTagList(area.tagName, 10); + for (int i = 0; i < areaTag.tagCount(); i++) { + NBTTagCompound stacksave = areaTag.getCompoundTagAt(i); + int slot = stacksave.getByte("Slot") & 0xFF; + if (!area.slots.contains(slot)) + continue; + + NEIClientUtils.setSlotContents(slot, ItemStack.loadItemStackFromNBT(stacksave), area.isContainer()); + } + } + } + + public static void saveState(int state) { + NBTTagCompound statesave = global.nbt.getCompoundTag("save" + state); + GuiContainer currentContainer = NEIClientUtils.getGuiContainer(); + LinkedList saveAreas = new LinkedList(); + saveAreas.add(new TaggedInventoryArea(NEIClientUtils.mc().thePlayer.inventory)); + + for (INEIGuiHandler handler : GuiInfo.guiHandlers) { + List areaList = handler.getInventoryAreas(currentContainer); + if (areaList != null) + saveAreas.addAll(areaList); + } + + for (TaggedInventoryArea area : saveAreas) { + NBTTagList areaTag = new NBTTagList(); + + for (int i : area.slots) { + ItemStack stack = area.getStackInSlot(i); + if (stack == null) + continue; + NBTTagCompound stacksave = new NBTTagCompound(); + stacksave.setByte("Slot", (byte) i); + stack.writeToNBT(stacksave); + areaTag.appendTag(stacksave); + } + statesave.setTag(area.tagName, areaTag); + } + + global.nbt.setTag("save" + state, statesave); + global.saveNBT(); + statesSaved[state] = true; + } + + public static boolean hasSMPCounterPart() { + return hasSMPCounterpart; + } + + public static void setHasSMPCounterPart(boolean flag) { + hasSMPCounterpart = flag; + permissableActions.clear(); + bannedBlocks.clear(); + disabledActions.clear(); + enabledActions.clear(); + } + + public static boolean canPerformAction(String name) { + if (!isEnabled()) + return false; + + if (!modePermitsAction(name)) + return false; + + String base = NEIActions.base(name); + if (hasSMPCounterpart) + return permissableActions.contains(base); + + if (NEIActions.smpRequired(name)) + return false; + + String cmd = getStringSetting("command." + base); + if (cmd == null || !cmd.startsWith("/")) + return false; + + return true; + } + + private static boolean modePermitsAction(String name) { + if (getCheatMode() == 0) return false; + if (getCheatMode() == 2) return true; + + String[] actions = getStringArrSetting("inventory.utilities"); + for (String action : actions) + if (action.equalsIgnoreCase(name)) + return true; + + return false; + } + + public static String[] getStringArrSetting(String s) { + return getStringSetting(s).replace(" ", "").split(","); + } + + public static void setInternalEnabled(boolean b) { + enabledOverride = b; + } + + public static void reloadSaves() { + File saveDir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/local"); + if (!saveDir.exists()) + return; + + List saves; + try { + saves = Minecraft.getMinecraft().getSaveLoader().getSaveList(); + } catch (Exception e) { + e.printStackTrace(); + return; + } + HashSet saveFileNames = new HashSet(); + for (SaveFormatComparator save : saves) + saveFileNames.add(save.getFileName()); + + for (File file : saveDir.listFiles()) { + if (file.isDirectory() && !saveFileNames.contains(file.getName())) + ObfuscationRun.deleteDir(file, true); + } + } +} diff --git a/src/codechicken/nei/NEIClientUtils.java b/src/codechicken/nei/NEIClientUtils.java new file mode 100644 index 00000000..c0f3f4cf --- /dev/null +++ b/src/codechicken/nei/NEIClientUtils.java @@ -0,0 +1,371 @@ +package codechicken.nei; + +import codechicken.lib.inventory.InventoryRange; +import codechicken.lib.inventory.InventoryUtils; +import codechicken.nei.api.GuiInfo; +import codechicken.nei.api.IInfiniteItemHandler; +import codechicken.nei.api.INEIGuiHandler; +import codechicken.nei.api.ItemInfo; +import com.google.common.collect.Iterables; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IChatComponent; +import org.lwjgl.input.Keyboard; + +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.util.*; + +import static codechicken.nei.NEIClientConfig.*; + +public class NEIClientUtils extends NEIServerUtils +{ + public static LangProxy lang = new LangProxy("nei"); + + public static Minecraft mc() { + return Minecraft.getMinecraft(); + } + + public static String translate(String key, Object... params) { + return lang.format(key, params); + } + + public static void printChatMessage(IChatComponent msg) { + if (mc().ingameGUI != null) + mc().ingameGUI.getChatGUI().printChatMessage(msg); + } + + public static void deleteHeldItem() { + deleteSlotStack(-999); + } + + public static void dropHeldItem() { + mc().playerController.windowClick(((GuiContainer) mc().currentScreen).inventorySlots.windowId, -999, 0, 0, mc().thePlayer); + } + + public static void deleteSlotStack(int slotNumber) { + setSlotContents(slotNumber, null, true); + } + + public static void decreaseSlotStack(int slotNumber) { + ItemStack stack = slotNumber == -999 ? getHeldItem() : mc().thePlayer.openContainer.getSlot(slotNumber).getStack(); + if (stack == null) + return; + + if (stack.stackSize == 1) + deleteSlotStack(slotNumber); + else { + stack = stack.copy(); + stack.stackSize--; + setSlotContents(slotNumber, stack, true); + } + } + + public static void deleteEverything() { + NEICPH.sendDeleteAllItems(); + } + + public static void deleteItemsOfType(ItemStack type) { + Container c = getGuiContainer().inventorySlots; + for (int i = 0; i < c.inventorySlots.size(); i++) { + Slot slot = c.getSlot(i); + if (slot == null) + continue; + + ItemStack stack = slot.getStack(); + if (stack != null && stack.getItem() == type.getItem() && stack.getItemDamage() == type.getItemDamage()) { + setSlotContents(i, null, true); + slot.putStack(null); + } + } + } + + public static ItemStack getHeldItem() { + return mc().thePlayer.inventory.getItemStack(); + } + + public static void setSlotContents(int slot, ItemStack item, boolean containerInv) { + NEICPH.sendSetSlot(slot, item, containerInv); + + if (slot == -999) + mc().thePlayer.inventory.setItemStack(item); + } + + /** + * @param typeStack + * @param button + * @param mode -1 = normal cheats, 0 = no infinites, 1 = replenish stack + */ + public static void cheatItem(ItemStack typeStack, int button, int mode) { + if (!canPerformAction("item") || typeStack.hasTagCompound() && !canPerformAction("itemnbt")) + return; + + if (mode == -1 && button == 0 && NEIClientUtils.shiftKey()) { + for (IInfiniteItemHandler handler : ItemInfo.infiniteHandlers) { + if (!handler.canHandleItem(typeStack)) + continue; + + ItemStack stack = handler.getInfiniteItem(typeStack); + if (stack != null) { + giveStack(stack, stack.stackSize, true); + return; + } + } + cheatItem(typeStack, button, 0); + } else if (button == 1) { + giveStack(typeStack, 1); + } else { + if (mode == 1 && typeStack.stackSize < typeStack.getMaxStackSize()) { + giveStack(typeStack, typeStack.getMaxStackSize() - typeStack.stackSize); + } else { + int amount = getItemQuantity(); + if (amount == 0) + amount = typeStack.getMaxStackSize(); + giveStack(typeStack, amount); + } + } + } + + public static void giveStack(ItemStack itemstack) { + giveStack(itemstack, itemstack.stackSize); + } + + public static void giveStack(ItemStack itemstack, int i) { + giveStack(itemstack, i, false); + } + + public static void giveStack(ItemStack itemstack, int i, boolean infinite) { + ItemStack itemstack1 = copyStack(itemstack, i); + if (hasSMPCounterPart()) { + ItemStack typestack = copyStack(itemstack1, 1); + if (!infinite && !canItemFitInInventory(mc().thePlayer, itemstack1) && (mc().currentScreen instanceof GuiContainer)) { + GuiContainer gui = getGuiContainer(); + List> handlerSlots = new LinkedList>(); + for(INEIGuiHandler handler : GuiInfo.guiHandlers) + handlerSlots.add(handler.getItemSpawnSlots(gui, typestack)); + + int increment = typestack.getMaxStackSize(); + int given = 0; + for(int slotNo : Iterables.concat(handlerSlots)) { + Slot slot = gui.inventorySlots.getSlot(slotNo); + if(!slot.isItemValid(typestack) || !InventoryUtils.canStack(slot.getStack(), typestack)) + continue; + + int qty = Math.min(itemstack1.stackSize - given, increment); + int current = slot.getHasStack() ? slot.getStack().stackSize : 0; + qty = Math.min(qty, slot.getSlotStackLimit() - current); + + ItemStack newStack = copyStack(typestack, qty + current); + slot.putStack(newStack); + setSlotContents(slotNo, newStack, true); + given += qty; + if(given >= itemstack1.stackSize) + break; + } + if(given > 0) + NEICPH.sendGiveItem(copyStack(typestack, given), false, false); + } else + NEICPH.sendGiveItem(itemstack1, infinite, true); + } else { + for (int given = 0; given < itemstack1.stackSize; ) { + int qty = Math.min(itemstack1.stackSize - given, itemstack1.getMaxStackSize()); + sendCommand(getStringSetting("command.item"), mc().thePlayer.getCommandSenderName(), itemstack1.getItem().getUnlocalizedName(), qty, itemstack1.getItemDamage()); + given += qty; + } + } + } + + public static void updateUnlimitedItems() { + ItemStack itemstack = getHeldItem(); + if (itemstack != null && itemstack.stackSize > 64) + itemstack.stackSize = 1; + + ItemStack aitemstack[] = mc().thePlayer.inventory.mainInventory; + for (int slot = 0; slot < aitemstack.length; slot++) { + ItemStack itemstack1 = aitemstack[slot]; + if (itemstack1 == null) + continue; + + if (itemstack1.stackSize < 0 || itemstack1.stackSize > 64) + itemstack1.stackSize = 111; + + if (itemstack1.getItemDamage() > -32000 && itemstack1.getItemDamage() < -30000) + itemstack1.setItemDamage(-32000); + } + } + + public static boolean itemListContains(ItemStack test) { + for (ItemStack stack : ItemList.itemMap.get(test.getItem())) + if (areStacksIdentical(stack, test)) + return true; + + return false; + } + + public static boolean canItemFitInInventory(EntityPlayer player, ItemStack itemstack) { + return InventoryUtils.getInsertibleQuantity(new InventoryRange(player.inventory, 0, 36), itemstack) > 0; + } + + public static boolean shiftKey() { + return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT); + } + + public static boolean controlKey() { + return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL); + } + + public static int getGamemode() { + if (invCreativeMode()) + return 2; + else if (mc().playerController.isInCreativeMode()) + return 1; + else if (mc().playerController.currentGameType.isAdventure()) + return 3; + else + return 0; + } + + public static boolean isValidGamemode(String s) { + return s.equals("survival") || + canPerformAction(s) && + Arrays.asList(getStringArrSetting("inventory.gamemodes")).contains(s); + } + + public static int getNextGamemode() { + int mode = getGamemode(); + int nmode = mode; + while (true) { + nmode = (nmode + 1) % NEIActions.gameModes.length; + if (nmode == mode || isValidGamemode(NEIActions.gameModes[nmode])) + break; + } + return nmode; + } + + public static void cycleGamemode() { + int mode = getGamemode(); + int nmode = getNextGamemode(); + if (mode == nmode) + return; + + if (hasSMPCounterPart()) + NEICPH.sendGamemode(nmode); + else + sendCommand(getStringSetting("command.creative"), getGameType(nmode), mc().thePlayer.getCommandSenderName()); + } + + public static long getTime() { + return mc().theWorld.getWorldInfo().getWorldTime(); + } + + public static void setTime(long l) { + mc().theWorld.getWorldInfo().setWorldTime(l); + } + + public static void setHourForward(int hour) { + long day = (getTime() / 24000L) * 24000L; + long newTime = day + 24000L + hour * 1000; + + if (hasSMPCounterPart()) + NEICPH.sendSetTime(hour); + else + sendCommand(getStringSetting("command.time"), newTime); + } + + public static void sendCommand(String command, Object... args) { + if (command.length() == 0) + return; + + NumberFormat numberformat = NumberFormat.getIntegerInstance(); + numberformat.setGroupingUsed(false); + MessageFormat messageformat = new MessageFormat(command); + for (int i = 0; i < args.length; i++) + if (args[i] instanceof Integer || args[i] instanceof Long) + messageformat.setFormatByArgumentIndex(i, numberformat); + + mc().thePlayer.sendChatMessage(messageformat.format(args)); + } + + public static boolean isRaining() { + return mc().theWorld.getWorldInfo().isRaining(); + } + + public static void toggleRaining() { + if (hasSMPCounterPart()) + NEICPH.sendToggleRain(); + else + sendCommand(getStringSetting("command.rain"), isRaining() ? 0 : 1); + } + + public static void healPlayer() { + if (hasSMPCounterPart()) + NEICPH.sendHeal(); + else + sendCommand(getStringSetting("command.heal"), mc().thePlayer.getCommandSenderName()); + } + + public static void toggleMagnetMode() { + if (hasSMPCounterPart()) + NEICPH.sendToggleMagnetMode(); + } + + public static ArrayList concatIntegersToRanges(List damages) { + ArrayList ranges = new ArrayList(); + if (damages.size() == 0) return ranges; + + Collections.sort(damages); + int start = -1; + int next = 0; + for (Integer i : damages) { + if (start == -1) { + start = next = i; + continue; + } + if (next + 1 != i) { + ranges.add(new int[]{start, next}); + start = next = i; + continue; + } + next = i; + } + ranges.add(new int[]{start, next}); + return ranges; + } + + public static ArrayList addIntegersToRanges(List ranges, List damages) { + for (int[] range : ranges) + for (int integer = range[0]; integer <= range[1]; integer++) + damages.add(integer); + + return concatIntegersToRanges(damages); + } + + public static boolean safeKeyDown(int keyCode) { + try { + return Keyboard.isKeyDown(keyCode); + } catch (IndexOutOfBoundsException e) { + return false; + } + } + + public static void setItemQuantity(int i) { + world.nbt.setInteger("quantity", i); + world.saveNBT(); + LayoutManager.quantity.setText(Integer.toString(i)); + } + + public static GuiContainer getGuiContainer() { + if (mc().currentScreen instanceof GuiContainer) + return (GuiContainer) mc().currentScreen; + + return null; + } + + public static boolean altKey() { + return Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU); + } +} diff --git a/src/codechicken/nei/NEICompatibility.java b/src/codechicken/nei/NEICompatibility.java new file mode 100644 index 00000000..46466b9d --- /dev/null +++ b/src/codechicken/nei/NEICompatibility.java @@ -0,0 +1,5 @@ +package codechicken.nei; + +public class NEICompatibility +{ +} diff --git a/src/codechicken/nei/NEIController.java b/src/codechicken/nei/NEIController.java new file mode 100644 index 00000000..a8c25ea4 --- /dev/null +++ b/src/codechicken/nei/NEIController.java @@ -0,0 +1,271 @@ +package codechicken.nei; + +import codechicken.nei.api.*; +import codechicken.nei.guihook.GuiContainerManager; +import codechicken.nei.guihook.IContainerInputHandler; +import codechicken.nei.guihook.IContainerSlotClickHandler; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.gui.inventory.GuiContainerCreative; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; +import net.minecraft.inventory.SlotCrafting; +import net.minecraft.item.ItemStack; + +import java.awt.*; +import java.util.LinkedList; + +import static codechicken.lib.gui.GuiDraw.getMousePosition; + +public class NEIController implements IContainerSlotClickHandler, IContainerInputHandler +{ + private static NEIController instance = new NEIController(); + + public static GuiContainerManager manager; + public static FastTransferManager fastTransferManager; + + private static boolean deleteMode; + private static int pickedUpFromSlot; + private static IInfiniteItemHandler heldStackInfinite; + + private static int selectedItem; + + public static void load() { + GuiContainerManager.addSlotClickHandler(instance); + GuiContainerManager.addInputHandler(instance); + } + + public static void load(GuiContainer gui) { + manager = GuiContainerManager.getManager(gui); + deleteMode = false; + GuiInfo.clearGuiHandlers(); + fastTransferManager = null; + if (!NEIClientConfig.isEnabled()) + return; + + fastTransferManager = new FastTransferManager(); + if (gui instanceof INEIGuiHandler) + API.registerNEIGuiHandler((INEIGuiHandler) gui); + } + + ItemStack firstheld; + + public static void updateUnlimitedItems(InventoryPlayer inventory) { + if (!NEIClientConfig.canPerformAction("item") || !NEIClientConfig.hasSMPCounterPart()) + return; + + LinkedList beforeStacks = new LinkedList(); + for (int i = 0; i < inventory.getSizeInventory(); i++) + beforeStacks.add(NEIServerUtils.copyStack(inventory.getStackInSlot(i))); + + for (int i = 0; i < inventory.getSizeInventory(); i++) { + ItemStack stack = inventory.getStackInSlot(i); + if (stack == null) + continue; + + for (IInfiniteItemHandler handler : ItemInfo.infiniteHandlers) + if (handler.canHandleItem(stack) && handler.isItemInfinite(stack)) + handler.replenishInfiniteStack(inventory, i); + } + + for (int i = 0; i < inventory.getSizeInventory(); i++) { + ItemStack newstack = inventory.getStackInSlot(i); + + if (!NEIServerUtils.areStacksIdentical(beforeStacks.get(i), newstack)) { + inventory.setInventorySlotContents(i, beforeStacks.get(i));//restore in case of SMP fail + NEIClientUtils.setSlotContents(i, newstack, false);//sends via SMP handler ;) + } + } + } + + public static void processCreativeCycling(InventoryPlayer inventory) { + if (NEIClientConfig.invCreativeMode() && NEIClientUtils.controlKey()) { + if (selectedItem != inventory.currentItem) { + if (inventory.currentItem == selectedItem + 1 || (inventory.currentItem == 0 && selectedItem == 8))//foward + { + NEICPH.sendCreativeScroll(1); + inventory.currentItem = selectedItem; + } else if (inventory.currentItem == selectedItem - 1 || (inventory.currentItem == 8 && selectedItem == 0)) { + NEICPH.sendCreativeScroll(-1); + inventory.currentItem = selectedItem; + } + } + } + + selectedItem = inventory.currentItem; + } + + @Override + public void beforeSlotClick(GuiContainer gui, int slotIndex, int button, Slot slot, int modifier) { + if (!NEIClientConfig.isEnabled()) + return; + + firstheld = NEIClientUtils.getHeldItem(); + } + + @Override + public boolean handleSlotClick(GuiContainer gui, int slotIndex, int button, Slot slot, int modifier, boolean eventconsumed) { + if (eventconsumed || !NEIClientConfig.isEnabled()) + return eventconsumed; + + if (deleteMode && slotIndex >= 0 && slot != null) { + if (NEIClientUtils.shiftKey() && button == 0) { + ItemStack itemstack1 = slot.getStack(); + if (itemstack1 != null) + NEIClientUtils.deleteItemsOfType(itemstack1); + + } else if (button == 1) + NEIClientUtils.decreaseSlotStack(slot.slotNumber); + else + NEIClientUtils.deleteSlotStack(slot.slotNumber); + return true; + } + + if (button == 1 && slot instanceof SlotCrafting)//right click + { + for (int i1 = 0; i1 < 64; i1++)//click this slot 64 times + manager.handleSlotClick(slot.slotNumber, button, 0); + return true; + } + + if (slotIndex >= 0 && NEIClientUtils.shiftKey() && NEIClientUtils.getHeldItem() != null && !slot.getHasStack()) { + ItemStack held = NEIClientUtils.getHeldItem(); + manager.handleSlotClick(slot.slotNumber, button, 0); + if (slot.isItemValid(held) && !ItemInfo.fastTransferExemptions.contains(slot.getClass())) + fastTransferManager.performMassTransfer(gui, pickedUpFromSlot, slotIndex, held); + + return true; + } + + if (NEIClientUtils.controlKey() + && slot != null && slot.getStack() != null + && NEIClientConfig.canPerformAction("item") + && slot.isItemValid(slot.getStack())) { + NEIClientUtils.cheatItem(slot.getStack(), button, 1); + return true; + } + + if (slotIndex == -999 && NEIClientUtils.shiftKey() && button == 0) { + fastTransferManager.throwAll(gui, pickedUpFromSlot); + return true; + } + + return false; + } + + @Override + public void afterSlotClick(GuiContainer gui, int slotIndex, int button, Slot slot, int modifier) { + if (!NEIClientConfig.isEnabled()) + return; + + ItemStack nowHeld = NEIClientUtils.getHeldItem(); + + if (firstheld != nowHeld) + pickedUpFromSlot = slotIndex; + + if (NEIClientConfig.canPerformAction("item") && NEIClientConfig.hasSMPCounterPart()) { + if (heldStackInfinite != null && slot != null && slot.inventory == NEIClientUtils.mc().thePlayer.inventory) { + ItemStack stack = slot.getStack(); + if (stack != null) { + heldStackInfinite.onPlaceInfinite(stack); + } + NEIClientUtils.setSlotContents(slotIndex, stack, true); + } + + if (firstheld != nowHeld) + heldStackInfinite = null; + + if (firstheld != nowHeld && nowHeld != null) { + for (IInfiniteItemHandler handler : ItemInfo.infiniteHandlers) { + if (handler.canHandleItem(nowHeld) && handler.isItemInfinite(nowHeld)) { + handler.onPickup(nowHeld); + NEIClientUtils.setSlotContents(-999, nowHeld, true); + heldStackInfinite = handler; + break; + } + } + } + } + } + + @Override + public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyCode) { + if (!NEIClientConfig.isEnabled()) + return false; + + Slot slot = GuiContainerManager.getSlotMouseOver(gui); + if (slot == null) + return false; + + int slotIndex = slot.slotNumber; + + if (keyCode == NEIClientUtils.mc().gameSettings.keyBindDrop.getKeyCode() && NEIClientUtils.shiftKey()) { + FastTransferManager.clickSlot(gui, slotIndex); + fastTransferManager.throwAll(gui, slotIndex); + FastTransferManager.clickSlot(gui, slotIndex); + + return true; + } + + return false; + } + + @Override + public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) { + if (!NEIClientConfig.isEnabled()) + return false; + + Point mousePos = getMousePosition(); + Slot mouseover = manager.window.getSlotAtPosition(mousePos.x, mousePos.y); + if (mouseover != null && mouseover.getHasStack()) { + if (scrolled > 0) + fastTransferManager.transferItem(manager.window, mouseover.slotNumber); + else + fastTransferManager.retrieveItem(manager.window, mouseover.slotNumber); + return true; + } + return false; + } + + @Override + public boolean keyTyped(GuiContainer gui, char keyChar, int keyCode) { + return false; + } + + @Override + public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) { + return false; + } + + @Override + public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) { + } + + @Override + public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) { + } + + @Override + public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) { + } + + @Override + public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) { + } + + @Override + public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) { + } + + public static boolean canUseDeleteMode() { + return !(NEIClientUtils.getGuiContainer() instanceof GuiContainerCreative); + } + + public static void toggleDeleteMode() { + if(canUseDeleteMode()) + deleteMode = !deleteMode; + } + + public static boolean getDeleteMode() { + return deleteMode; + } +} diff --git a/src/codechicken/nei/NEICreativeGuiHandler.java b/src/codechicken/nei/NEICreativeGuiHandler.java new file mode 100644 index 00000000..ccd1a02b --- /dev/null +++ b/src/codechicken/nei/NEICreativeGuiHandler.java @@ -0,0 +1,22 @@ +package codechicken.nei; + +import codechicken.nei.api.INEIGuiAdapter; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.gui.inventory.GuiContainerCreative; +import net.minecraft.creativetab.CreativeTabs; + +public class NEICreativeGuiHandler extends INEIGuiAdapter +{ + @Override + public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) + { + if(!(gui instanceof GuiContainerCreative)) + return currentVisibility; + + if(((GuiContainerCreative)gui).func_147056_g() != CreativeTabs.tabInventory.getTabIndex()) + currentVisibility.showItemSection = currentVisibility.enableDeleteMode = false; + + return currentVisibility; + } + +} diff --git a/src/codechicken/nei/NEIDummySlotHandler.java b/src/codechicken/nei/NEIDummySlotHandler.java new file mode 100644 index 00000000..0c658e94 --- /dev/null +++ b/src/codechicken/nei/NEIDummySlotHandler.java @@ -0,0 +1,24 @@ +package codechicken.nei; + +import codechicken.core.inventory.ContainerExtended; +import codechicken.core.inventory.SlotDummy; +import codechicken.nei.api.INEIGuiAdapter; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +public class NEIDummySlotHandler extends INEIGuiAdapter +{ + @Override + public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) + { + Slot slot = gui.getSlotAtPosition(mousex, mousey); + if(slot instanceof SlotDummy && slot.isItemValid(draggedStack) && gui.inventorySlots instanceof ContainerExtended) + { + ((SlotDummy)slot).slotClick(draggedStack, button, NEIClientUtils.shiftKey()); + NEICPH.sendDummySlotSet(slot.slotNumber, slot.getStack()); + return true; + } + return false; + } +} diff --git a/src/codechicken/nei/NEIModContainer.java b/src/codechicken/nei/NEIModContainer.java new file mode 100644 index 00000000..d5ca87b8 --- /dev/null +++ b/src/codechicken/nei/NEIModContainer.java @@ -0,0 +1,94 @@ +package codechicken.nei; + +import codechicken.core.CommonUtils; +import codechicken.core.launch.CodeChickenCorePlugin; +import codechicken.nei.api.IConfigureNEI; +import codechicken.nei.asm.NEICorePlugin; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import cpw.mods.fml.client.FMLFileResourcePack; +import cpw.mods.fml.client.FMLFolderResourcePack; +import cpw.mods.fml.common.DummyModContainer; +import cpw.mods.fml.common.LoadController; +import cpw.mods.fml.common.MetadataCollection; +import cpw.mods.fml.common.ModMetadata; +import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.versioning.ArtifactVersion; +import cpw.mods.fml.common.versioning.VersionParser; +import cpw.mods.fml.common.versioning.VersionRange; +import net.minecraft.util.EnumChatFormatting; + +import java.io.File; +import java.util.*; + +public class NEIModContainer extends DummyModContainer +{ + public static LinkedList plugins = new LinkedList(); + + public NEIModContainer() { + super(MetadataCollection.from(MetadataCollection.class.getResourceAsStream("/neimod.info"), "NotEnoughItems").getMetadataForId("NotEnoughItems", null)); + getMetadata(); + } + + @Override + public Set getRequirements() { + Set deps = new HashSet(); + deps.add(VersionParser.parseVersionReference("CodeChickenCore@[0,)")); + return deps; + } + + @Override + public List getDependencies() { + return new LinkedList(getRequirements()); + } + + @Override + public ModMetadata getMetadata() { + ModMetadata meta = super.getMetadata(); + meta.description = "Recipe Viewer, Inventory Manager, Item Spawner, Cheats and more.\n"+ EnumChatFormatting.WHITE+"\n"; + + if (plugins.size() == 0) { + meta.description += EnumChatFormatting.RED+"No installed plugins."; + } else { + meta.description += EnumChatFormatting.GREEN+"Installed plugins: "; + for (int i = 0; i < plugins.size(); i++) { + if (i > 0) + meta.description += ", "; + IConfigureNEI plugin = plugins.get(i); + meta.description += plugin.getName() + " " + plugin.getVersion(); + } + meta.description += "."; + } + + return meta; + } + + @Override + public boolean registerBus(EventBus bus, LoadController controller) { + bus.register(this); + return true; + } + + @Subscribe + public void init(FMLInitializationEvent event) { + if (CommonUtils.isClient()) + ClientHandler.load(); + + ServerHandler.load(); + } + + @Override + public VersionRange acceptableMinecraftVersionRange() { + return VersionParser.parseRange(CodeChickenCorePlugin.mcVersion); + } + + /*@Override + public File getSource() { + return NEICorePlugin.location; + } + + @Override + public Class getCustomResourcePackClass() { + return getSource().isDirectory() ? FMLFolderResourcePack.class : FMLFileResourcePack.class; + }*/ +} diff --git a/src/codechicken/nei/NEISPH.java b/src/codechicken/nei/NEISPH.java new file mode 100644 index 00000000..bacf3da0 --- /dev/null +++ b/src/codechicken/nei/NEISPH.java @@ -0,0 +1,264 @@ +package codechicken.nei; + +import codechicken.core.CommonUtils; +import codechicken.core.IGuiPacketSender; +import codechicken.core.ServerUtils; +import codechicken.core.inventory.ContainerExtended; +import codechicken.core.inventory.SlotDummy; +import codechicken.lib.packet.PacketCustom; +import codechicken.lib.packet.PacketCustom.IServerPacketHandler; +import codechicken.lib.vec.BlockCoord; +import cpw.mods.fml.relauncher.Side; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.InventoryBasic; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.network.play.INetHandlerPlayServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityMobSpawner; +import net.minecraft.world.World; + +import java.util.LinkedList; +import java.util.Set; + +public class NEISPH implements IServerPacketHandler +{ + @Override + public void handlePacket(PacketCustom packet, EntityPlayerMP sender, INetHandlerPlayServer netHandler) { + if (!NEIServerConfig.authenticatePacket(sender, packet)) + return; + + switch (packet.getType()) { + case 1: + handleGiveItem(sender, packet); + break; + case 4: + NEIServerUtils.deleteAllItems(sender); + break; + case 5: + setInventorySlot(sender, packet); + break; + case 6: + NEIServerUtils.toggleMagnetMode(sender); + break; + case 7: + NEIServerUtils.setHourForward(sender.worldObj, packet.readUByte(), true); + break; + case 8: + NEIServerUtils.healPlayer(sender); + break; + case 9: + NEIServerUtils.toggleRaining(sender.worldObj, true); + break; + case 10: + sendLoginState(sender); + break; + case 11: + sender.sendContainerAndContentsToPlayer(sender.openContainer, sender.openContainer.getInventory()); + break; + case 12: + handlePropertyChange(sender, packet); + break; + case 13: + NEIServerUtils.setGamemode(sender, packet.readUByte()); + break; + case 14: + NEIServerUtils.cycleCreativeInv(sender, packet.readInt()); + break; + case 15: + handleMobSpawnerID(sender.worldObj, packet.readCoord(), packet.readString()); + break; + case 20: + handleContainerPacket(sender, packet); + break; + case 21: + openEnchantmentGui(sender); + break; + case 22: + modifyEnchantment(sender, packet.readUByte(), packet.readUByte(), packet.readBoolean()); + break; + case 23: + processCreativeInv(sender, packet.readBoolean()); + break; + case 24: + openPotionGui(sender, packet); + break; + case 25: + handleDummySlotSet(sender, packet); + break; + } + } + + private void handleDummySlotSet(EntityPlayerMP sender, PacketCustom packet) { + int slotNumber = packet.readShort(); + ItemStack stack = packet.readItemStack(true); + + Slot slot = sender.openContainer.getSlot(slotNumber); + if (slot instanceof SlotDummy) + slot.putStack(stack); + } + + private void handleContainerPacket(EntityPlayerMP sender, PacketCustom packet) { + if (sender.openContainer instanceof ContainerExtended) + ((ContainerExtended) sender.openContainer).handleInputPacket(packet); + } + + private void handleMobSpawnerID(World world, BlockCoord coord, String mobtype) { + TileEntity tile = world.getTileEntity(coord.x, coord.y, coord.z); + if (tile instanceof TileEntityMobSpawner) { + ((TileEntityMobSpawner) tile).func_145881_a().setEntityName(mobtype); + tile.markDirty(); + world.markBlockForUpdate(coord.x, coord.y, coord.z); + } + } + + private void handlePropertyChange(EntityPlayerMP sender, PacketCustom packet) { + String name = packet.readString(); + if (NEIServerConfig.canPlayerPerformAction(sender.getCommandSenderName(), name)) + NEIServerConfig.disableAction(sender.dimension, name, packet.readBoolean()); + } + + private void processCreativeInv(EntityPlayerMP sender, boolean open) { + if (open) { + ServerUtils.openSMPContainer(sender, new ContainerCreativeInv(sender, new ExtendedCreativeInv(NEIServerConfig.forPlayer(sender.getCommandSenderName()), Side.SERVER)), new IGuiPacketSender() + { + @Override + public void sendPacket(EntityPlayerMP player, int windowId) { + PacketCustom packet = new PacketCustom(channel, 23); + packet.writeBoolean(true); + packet.writeByte(windowId); + packet.sendToPlayer(player); + } + }); + } else { + sender.closeContainer(); + PacketCustom packet = new PacketCustom(channel, 23); + packet.writeBoolean(false); + packet.sendToPlayer(sender); + } + } + + private void handleGiveItem(EntityPlayerMP player, PacketCustom packet) { + NEIServerUtils.givePlayerItem(player, packet.readItemStack(true), packet.readBoolean(), packet.readBoolean()); + } + + private void setInventorySlot(EntityPlayerMP player, PacketCustom packet) { + boolean container = packet.readBoolean(); + int slot = packet.readShort(); + ItemStack item = packet.readItemStack(); + + ItemStack old = NEIServerUtils.getSlotContents(player, slot, container); + boolean deleting = item == null || old != null && NEIServerUtils.areStacksSameType(item, old) && item.stackSize < old.stackSize; + if (NEIServerConfig.canPlayerPerformAction(player.getCommandSenderName(), deleting ? "delete" : "item")) + NEIServerUtils.setSlotContents(player, slot, item, container); + } + + private void modifyEnchantment(EntityPlayerMP player, int e, int lvl, boolean add) { + ContainerEnchantmentModifier containerem = (ContainerEnchantmentModifier) player.openContainer; + if (add) { + containerem.addEnchantment(e, lvl); + } else { + containerem.removeEnchantment(e); + } + } + + private void openEnchantmentGui(EntityPlayerMP player) { + ServerUtils.openSMPContainer(player, new ContainerEnchantmentModifier(player.inventory, player.worldObj, 0, 0, 0), new IGuiPacketSender() + { + @Override + public void sendPacket(EntityPlayerMP player, int windowId) { + PacketCustom packet = new PacketCustom(channel, 21); + packet.writeByte(windowId); + packet.sendToPlayer(player); + } + }); + } + + private void openPotionGui(EntityPlayerMP player, PacketCustom packet) { + InventoryBasic b = new InventoryBasic("potionStore", true, 9); + for (int i = 0; i < b.getSizeInventory(); i++) + b.setInventorySlotContents(i, packet.readItemStack()); + ServerUtils.openSMPContainer(player, new ContainerPotionCreator(player.inventory, b), new IGuiPacketSender() + { + @Override + public void sendPacket(EntityPlayerMP player, int windowId) { + PacketCustom packet = new PacketCustom(channel, 24); + packet.writeByte(windowId); + packet.sendToPlayer(player); + } + }); + } + + public static void sendActionDisabled(int dim, String name, boolean disable) { + new PacketCustom(channel, 11) + .writeString(name) + .writeBoolean(disable) + .sendToDimension(dim); + } + + public static void sendActionEnabled(EntityPlayerMP player, String name, boolean enable) { + new PacketCustom(channel, 12) + .writeString(name) + .writeBoolean(enable) + .sendToPlayer(player); + } + + private void sendLoginState(EntityPlayerMP player) { + LinkedList actions = new LinkedList(); + LinkedList disabled = new LinkedList(); + LinkedList enabled = new LinkedList(); + LinkedList bannedItems = new LinkedList(); + PlayerSave playerSave = NEIServerConfig.forPlayer(player.getCommandSenderName()); + + for (String name : NEIActions.nameActionMap.keySet()) { + if (NEIServerConfig.canPlayerPerformAction(player.getCommandSenderName(), name)) + actions.add(name); + if (NEIServerConfig.isActionDisabled(player.dimension, name)) + disabled.add(name); + if (playerSave.isActionEnabled(name)) + enabled.add(name); + } + for (ItemStackMap.Entry> entry : NEIServerConfig.bannedItems.entries()) + if (!NEIServerConfig.isPlayerInList(player.getCommandSenderName(), entry.value, true)) + bannedItems.add(entry.key); + + PacketCustom packet = new PacketCustom(channel, 10); + + packet.writeByte(actions.size()); + for (String s : actions) + packet.writeString(s); + + packet.writeByte(disabled.size()); + for (String s : disabled) + packet.writeString(s); + + packet.writeByte(enabled.size()); + for (String s : enabled) + packet.writeString(s); + + packet.writeInt(bannedItems.size()); + for (ItemStack stack : bannedItems) + packet.writeItemStack(stack); + + packet.sendToPlayer(player); + } + + public static void sendHasServerSideTo(EntityPlayerMP player) { + NEIServerConfig.logger.debug("Sending serverside check to: " + player.getCommandSenderName()); + PacketCustom packet = new PacketCustom(channel, 1); + packet.writeByte(NEIActions.protocol); + packet.writeString(CommonUtils.getWorldName(player.worldObj)); + + packet.sendToPlayer(player); + } + + public static void sendAddMagneticItemTo(EntityPlayerMP player, EntityItem item) { + PacketCustom packet = new PacketCustom(channel, 13); + packet.writeInt(item.getEntityId()); + + packet.sendToPlayer(player); + } + + public static final String channel = "NEI"; +} diff --git a/src/codechicken/nei/NEIServerConfig.java b/src/codechicken/nei/NEIServerConfig.java new file mode 100644 index 00000000..13395123 --- /dev/null +++ b/src/codechicken/nei/NEIServerConfig.java @@ -0,0 +1,285 @@ +package codechicken.nei; + +import codechicken.core.CommonUtils; +import codechicken.core.ServerUtils; +import codechicken.lib.config.ConfigFile; +import codechicken.lib.inventory.InventoryUtils; +import codechicken.lib.packet.PacketCustom; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.JsonToNBT; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.FileReader; +import java.io.PrintWriter; +import java.util.*; + +public class NEIServerConfig +{ + private static MinecraftServer server; + + public static Logger logger = LogManager.getLogger("NotEnoughItems"); + public static File saveDir; + public static ConfigFile serverConfig; + public static Map dimTags = new HashMap(); + public static HashMap playerSaves = new HashMap(); + public static ItemStackMap> bannedItems = new ItemStackMap>(); + + public static void load(World world) { + if (MinecraftServer.getServer() != server) { + logger.debug("Loading NEI Server"); + server = MinecraftServer.getServer(); + saveDir = new File(DimensionManager.getCurrentSaveRootDirectory(), "NEI"); + + dimTags.clear(); + loadConfig(); + loadBannedItems(); + } + loadWorld(world); + } + + public static File getSaveDir(int dim) { + return new File(CommonUtils.getSaveLocation(dim), "NEI"); + } + + private static void loadWorld(World world) { + try { + File file = new File(getSaveDir(CommonUtils.getDimension(world)), "world.dat"); + NBTTagCompound tag = CompressedStreamTools.read(file); + if(tag == null) + tag = new NBTTagCompound(); + dimTags.put(CommonUtils.getDimension(world), tag); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void loadConfig() { + File file = new File(saveDir, "server.cfg"); + serverConfig = new ConfigFile(file); + + serverConfig.setNewLineMode(1); + serverConfig.getTag("permissions").useBraces(); + serverConfig.getTag("permissions").setComment("List of players who can use these features.\nEg. time=CodeChicken, Friend1"); + + setDefaultFeature("time"); + setDefaultFeature("rain"); + setDefaultFeature("heal"); + setDefaultFeature("magnet"); + setDefaultFeature("creative"); + setDefaultFeature("creative+"); + setDefaultFeature("adventure"); + setDefaultFeature("enchant"); + setDefaultFeature("potion"); + setDefaultFeature("save-state"); + setDefaultFeature("item"); + setDefaultFeature("delete"); + setDefaultFeature("notify-item", "CONSOLE, OP"); + } + + private static void setDefaultFeature(String featurename, String... names) { + if (names.length == 0) + names = new String[]{"OP"}; + + String list = ""; + for (int i = 0; i < names.length; i++) { + if (i >= 1) + list += ", "; + list += names[i]; + } + serverConfig.getTag("permissions." + featurename).setDefaultValue(list); + } + + private static void saveWorld(int dim) { + try { + File file = new File(getSaveDir(dim), "world.dat"); + CompressedStreamTools.write(dimTags.get(dim), file); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static boolean canPlayerPerformAction(String playername, String name) { + return isPlayerInList(playername, getPlayerList("permissions." + NEIActions.base(name)), true); + } + + public static boolean isPlayerInList(String playername, Set list, boolean allowCards) { + if (playername.equals("CONSOLE")) + return list.contains(playername); + + playername = playername.toLowerCase(); + + if (allowCards) { + if (list.contains("ALL")) + return true; + if ((ServerUtils.isPlayerOP(playername) || ServerUtils.isPlayerOwner(playername)) && list.contains("OP")) + return true; + } + + return list.contains(playername); + } + + public static boolean isActionDisabled(int dim, String name) { + return dimTags.get(dim).getBoolean("disabled" + name); + } + + public static void disableAction(int dim, String name, boolean disable) { + dimTags.get(dim).setBoolean("disabled" + name, disable); + NEISPH.sendActionDisabled(dim, name, disable); + saveWorld(dim); + } + + public static HashSet getPlayerList(String tag) { + String[] list = serverConfig.getTag(tag).getValue("").replace(" ", "").split(","); + return new HashSet(Arrays.asList(list)); + } + + public static void addPlayerToList(String playername, String tag) { + HashSet list = getPlayerList(tag); + + if (!playername.equals("CONSOLE") && !playername.equals("ALL") && !playername.equals("OP")) + playername = playername.toLowerCase(); + + list.add(playername); + savePlayerList(tag, list); + } + + public static void remPlayerFromList(String playername, String tag) { + HashSet list = getPlayerList(tag); + + if (!playername.equals("CONSOLE") && !playername.equals("ALL") && !playername.equals("OP")) + playername = playername.toLowerCase(); + + list.remove(playername); + savePlayerList(tag, list); + } + + private static void savePlayerList(String tag, Collection list) { + StringBuilder sb = new StringBuilder(); + int i = 0; + for (Iterator iterator = list.iterator(); iterator.hasNext(); i++) { + if (i != 0) + sb.append(", "); + + sb.append(iterator.next()); + } + + serverConfig.getTag(tag).setValue(sb.toString()); + } + + private static void loadBannedItems() { + bannedItems.clear(); + File file = new File(saveDir, "banneditems.cfg"); + if(!file.exists()) { + bannedItems.put(new ItemStack(Blocks.bedrock), new HashSet(Arrays.asList("NONE"))); + saveBannedItems(); + return; + } + try { + FileReader r = new FileReader(file); + int line = 0; + for(String s : IOUtils.readLines(r)) { + if(s.charAt(0) == '#' || s.trim().length() == 0) + continue; + int delim = s.lastIndexOf('='); + if(delim < 0) { + System.err.println("line "+line+": Missing ="); + continue; + } + try { + NBTTagCompound key = (NBTTagCompound) JsonToNBT.func_150315_a(s.substring(0, delim)); + Set values = new HashSet(); + for(String s2 : s.substring(delim+1).split(",")) + values.add(s2.trim()); + bannedItems.put(InventoryUtils.loadPersistant(key), values); + } catch (Exception e) { + System.err.println("line "+line+": "+e.getMessage()); + } + } + r.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void saveBannedItems() { + File file = new File(saveDir, "banneditems.cfg"); + try { + if(!file.exists()) + file.createNewFile(); + + PrintWriter p = new PrintWriter(file); + p.println("#Saved in this format for external editing. The format isn't that hard to figure out. If you think you're up to it, modify it here!"); + + for (ItemStackMap.Entry> entry : bannedItems.entries()) { + NBTTagCompound key = InventoryUtils.savePersistant(entry.key, new NBTTagCompound()); + key.removeTag("Count"); + if(key.getByte("Damage") == 0) key.removeTag("Damage"); + + p.print(key.toString()); + p.print("=["); + int i = 0; + for (String s : entry.value) { + if(i++ != 0) p.print(", "); + p.print(s); + } + p.println("]"); + } + p.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static PlayerSave forPlayer(String username) { + return playerSaves.get(username); + } + + public static void loadPlayer(EntityPlayer player) { + logger.debug("Loading Player: " + player.getGameProfile().getName()); + playerSaves.put(player.getCommandSenderName(), new PlayerSave(player.getCommandSenderName(), new File(saveDir, "players"))); + } + + public static void unloadPlayer(EntityPlayer player) { + logger.debug("Unloading Player: " + player.getCommandSenderName()); + PlayerSave playerSave = playerSaves.remove(player.getCommandSenderName()); + if (playerSave != null) + playerSave.save(); + } + + public static boolean authenticatePacket(EntityPlayerMP sender, PacketCustom packet) { + switch (packet.getType()) { + case 1: + return canPlayerPerformAction(sender.getCommandSenderName(), "item"); + case 4: + return canPlayerPerformAction(sender.getCommandSenderName(), "delete"); + case 6: + return canPlayerPerformAction(sender.getCommandSenderName(), "magnet"); + case 7: + return canPlayerPerformAction(sender.getCommandSenderName(), "time"); + case 8: + return canPlayerPerformAction(sender.getCommandSenderName(), "heal"); + case 9: + return canPlayerPerformAction(sender.getCommandSenderName(), "rain"); + case 14: + case 23: + return canPlayerPerformAction(sender.getCommandSenderName(), "creative+"); + case 21: + case 22: + return canPlayerPerformAction(sender.getCommandSenderName(), "enchant"); + case 24: + return canPlayerPerformAction(sender.getCommandSenderName(), "potion"); + } + return true; + } +} diff --git a/src/codechicken/nei/NEIServerUtils.java b/src/codechicken/nei/NEIServerUtils.java new file mode 100644 index 00000000..73e5b2ec --- /dev/null +++ b/src/codechicken/nei/NEIServerUtils.java @@ -0,0 +1,363 @@ +package codechicken.nei; + +import codechicken.core.CommonUtils; +import codechicken.core.ServerUtils; +import codechicken.lib.inventory.InventoryUtils; +import codechicken.lib.packet.PacketCustom; +import net.minecraft.command.ICommandSender; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraft.world.WorldSettings.GameType; +import net.minecraftforge.oredict.OreDictionary; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.List; + +public class NEIServerUtils +{ + public static boolean isRaining(World world) { + return world.getWorldInfo().isRaining(); + } + + public static void toggleRaining(World world, boolean notify) { + boolean raining = !world.isRaining(); + if (!raining)//turn off + ((WorldServer) world).provider.resetRainAndThunder(); + else + world.getWorldInfo().setRaining(!isRaining(world)); + + if (notify) + ServerUtils.sendChatToAll(new ChatComponentTranslation("nei.chat.rain." + (raining ? "on" : "off"))); + } + + public static void healPlayer(EntityPlayer player) { + player.heal(20); + player.getFoodStats().addStats(20, 1); + player.extinguish(); + } + + public static long getTime(World world) { + return world.getWorldInfo().getWorldTime(); + } + + public static void setTime(long l, World world) { + world.getWorldInfo().setWorldTime(l); + } + + public static void setSlotContents(EntityPlayer player, int slot, ItemStack item, boolean containerInv) { + if (slot == -999) + player.inventory.setItemStack(item); + else if (containerInv) + player.openContainer.putStackInSlot(slot, item); + else + player.inventory.setInventorySlotContents(slot, item); + } + + public static ItemStack getSlotContents(EntityPlayer player, int slot, boolean containerInv) { + if (slot == -999) + return player.inventory.getItemStack(); + else if (containerInv) + return player.openContainer.getSlot(slot).getStack(); + else + return player.inventory.getStackInSlot(slot); + } + + @SuppressWarnings("unchecked") + public static void deleteAllItems(EntityPlayerMP player) { + for (Slot slot : (List) player.openContainer.inventorySlots) + slot.putStack(null); + + player.sendContainerAndContentsToPlayer(player.openContainer, player.openContainer.getInventory()); + } + + public static void setHourForward(World world, int hour, boolean notify) { + long day = (getTime(world) / 24000L) * 24000L; + long newTime = day + 24000L + hour * 1000; + setTime(newTime, world); + if (notify) + ServerUtils.sendChatToAll(new ChatComponentTranslation("nei.chat.time", getTime(world) / 24000L, hour)); + } + + public static void advanceDisabledTimes(World world) { + int dim = CommonUtils.getDimension(world); + int hour = (int) (getTime(world) % 24000) / 1000; + int newhour = hour; + while (NEIServerConfig.isActionDisabled(dim, NEIActions.timeZones[newhour / 6])) + newhour = ((newhour / 6 + 1) % 4) * 6; + + if (newhour != hour) + setHourForward(world, newhour, false); + } + + public static boolean canItemFitInInventory(EntityPlayer player, ItemStack itemstack) { + return InventoryUtils.insertItem(player.inventory, itemstack, true) == 0; + } + + public static void sendNotice(ICommandSender sender, IChatComponent msg, String permission) { + ChatComponentTranslation notice = new ChatComponentTranslation("chat.type.admin", sender.getCommandSenderName(), msg.createCopy()); + notice.getChatStyle().setColor(EnumChatFormatting.GRAY).setItalic(true); + + if (NEIServerConfig.canPlayerPerformAction("CONSOLE", permission)) + MinecraftServer.getServer().addChatMessage(notice); + + for (EntityPlayer p : ServerUtils.getPlayers()) + if(p == sender) + p.addChatComponentMessage(msg); + else if (NEIServerConfig.canPlayerPerformAction(p.getCommandSenderName(), permission)) + p.addChatComponentMessage(notice); + } + + /** + * @param stack1 The {@link ItemStack} being compared. + * @param stack2 The {@link ItemStack} to compare to. + * @return whether the two items are the same in terms of damage and itemID. + */ + public static boolean areStacksSameType(ItemStack stack1, ItemStack stack2) { + if (stack1 == null || stack2 == null) + return stack1 == stack2; + + return InventoryUtils.canStack(stack1, stack2); + } + + /** + * {@link ItemStack}s with damage -1 are wildcards allowing all damages. Eg all colours of wool are allowed to create Beds. + * + * @param stack1 The {@link ItemStack} being compared. + * @param stack2 The {@link ItemStack} to compare to. + * @return whether the two items are the same from the perspective of a crafting inventory. + */ + public static boolean areStacksSameTypeCrafting(ItemStack stack1, ItemStack stack2) { + if (stack1 == null || stack2 == null) + return false; + + return stack1.getItem() == stack2.getItem() && + (stack1.getItemDamage() == stack2.getItemDamage() || stack1.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack2.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack1.getItem().isDamageable()); + } + + /** + * A simple function for comparing ItemStacks in a compatible with comparators. + * + * @param stack1 The {@link ItemStack} being compared. + * @param stack2 The {@link ItemStack} to compare to. + * @return The ordering of stack1 relative to stack2. + */ + public static int compareStacks(ItemStack stack1, ItemStack stack2) { + if (stack1 == stack2) return 0;//catches both null + if (stack1 == null || stack2 == null) return stack1 == null ? -1 : 1;//null stack goes first + if (stack1.getItem() != stack2.getItem()) return Item.getIdFromItem(stack1.getItem()) - Item.getIdFromItem(stack2.getItem()); + if (stack1.stackSize != stack2.stackSize) return stack1.stackSize - stack2.stackSize; + return stack1.getItemDamage() - stack2.getItemDamage(); + } + + public static boolean areStacksIdentical(ItemStack stack1, ItemStack stack2) { + return compareStacks(stack1, stack2) == 0; + } + + public static IChatComponent setColour(IChatComponent msg, EnumChatFormatting colour) { + msg.getChatStyle().setColor(colour); + return msg; + } + + public static void givePlayerItem(EntityPlayerMP player, ItemStack stack, boolean infinite, boolean doGive) { + if (stack.getItem() == null) { + player.addChatComponentMessage(setColour(new ChatComponentTranslation("nei.chat.give.noitem"), EnumChatFormatting.WHITE)); + return; + } + + int given = stack.stackSize; + if(doGive) { + if (infinite) + player.inventory.addItemStackToInventory(stack); + else + given -= InventoryUtils.insertItem(player.inventory, stack, false); + } + + sendNotice(player, new ChatComponentTranslation("commands.give.success", stack.func_151000_E(), infinite ? "\u221E" : Integer.toString(given), player.getCommandSenderName()), "notify-item"); + player.openContainer.detectAndSendChanges(); + } + + public static ItemStack copyStack(ItemStack itemstack, int i) { + if (itemstack == null) + return null; + + itemstack.stackSize += i; + return itemstack.splitStack(i); + } + + public static ItemStack copyStack(ItemStack itemstack) { + if (itemstack == null) + return null; + + return copyStack(itemstack, itemstack.stackSize); + } + + public static void toggleMagnetMode(EntityPlayerMP player) { + PlayerSave playerSave = NEIServerConfig.forPlayer(player.getCommandSenderName()); + playerSave.enableAction("magnet", !playerSave.isActionEnabled("magnet")); + } + + public static int getCreativeMode(EntityPlayerMP player) { + if (NEIServerConfig.forPlayer(player.getCommandSenderName()).isActionEnabled("creative+")) + return 2; + else if (player.theItemInWorldManager.isCreative()) + return 1; + else if (player.theItemInWorldManager.getGameType().isAdventure()) + return 3; + else + return 0; + } + + public static GameType getGameType(int mode) { + switch (mode) { + case 0: + return GameType.SURVIVAL; + case 1: + case 2: + return GameType.CREATIVE; + case 3: + return GameType.ADVENTURE; + } + return null; + } + + public static void setGamemode(EntityPlayerMP player, int mode) { + if (mode < 0 || mode >= NEIActions.gameModes.length || + NEIActions.nameActionMap.containsKey(NEIActions.gameModes[mode]) && + !NEIServerConfig.canPlayerPerformAction(player.getCommandSenderName(), NEIActions.gameModes[mode])) + return; + + //change it on the server + player.theItemInWorldManager.setGameType(getGameType(mode)); + NEIServerConfig.forPlayer(player.getCommandSenderName()).enableAction("creative+", mode == 2); + + //tell the client to change it + new PacketCustom(NEISPH.channel, 14).writeByte(mode).sendToPlayer(player); + player.addChatMessage(new ChatComponentTranslation("nei.chat.gamemode." + mode)); + } + + public static void cycleCreativeInv(EntityPlayerMP player, int steps) { + InventoryPlayer inventory = player.inventory; + + //top down [row][col] + ItemStack[][] slots = new ItemStack[10][9]; + PlayerSave playerSave = NEIServerConfig.forPlayer(player.getCommandSenderName()); + + //get + System.arraycopy(inventory.mainInventory, 0, slots[9], 0, 9); + + for (int row = 0; row < 3; row++) + System.arraycopy(inventory.mainInventory, (row + 1) * 9, slots[row + 6], 0, 9); + + for (int row = 0; row < 6; row++) + System.arraycopy(playerSave.creativeInv, row * 9, slots[row], 0, 9); + + ItemStack[][] newslots = new ItemStack[10][]; + + //put back + for (int row = 0; row < 10; row++) + newslots[(row + steps + 10) % 10] = slots[row]; + + System.arraycopy(newslots[9], 0, inventory.mainInventory, 0, 9); + + for (int row = 0; row < 3; row++) + System.arraycopy(newslots[row + 6], 0, inventory.mainInventory, (row + 1) * 9, 9); + + for (int row = 0; row < 6; row++) + System.arraycopy(newslots[row], 0, playerSave.creativeInv, row * 9, 9); + + playerSave.setDirty(); + } + + public static List getEnchantments(ItemStack itemstack) { + ArrayList arraylist = new ArrayList(); + if (itemstack != null) { + NBTTagList nbttaglist = itemstack.getEnchantmentTagList(); + if (nbttaglist != null) + for (int i = 0; i < nbttaglist.tagCount(); i++) { + NBTTagCompound tag = nbttaglist.getCompoundTagAt(i); + arraylist.add(new int[]{tag.getShort("id"), tag.getShort("lvl")}); + } + } + return arraylist; + } + + public static boolean stackHasEnchantment(ItemStack itemstack, int e) { + List allenchantments = getEnchantments(itemstack); + for (int[] ai : allenchantments) + if (ai[0] == e) + return true; + + return false; + } + + public static int getEnchantmentLevel(ItemStack itemstack, int e) { + List allenchantments = getEnchantments(itemstack); + for (int[] ai : allenchantments) + if (ai[0] == e) + return ai[1]; + + return -1; + } + + public static boolean doesEnchantmentConflict(List enchantments, Enchantment e) { + for (int[] ai : enchantments) + if (!e.canApplyTogether(Enchantment.enchantmentsList[ai[0]])) + return true; + + return false; + } + + public static RuntimeException throwCME(String msg) { + if (CommonUtils.isClient()) + return ClientHandler.throwCME(msg); + + throw new RuntimeException(msg); + } + + @SuppressWarnings("unchecked") + public static ItemStack[] extractRecipeItems(Object obj) { + if (obj instanceof ItemStack) + return new ItemStack[]{(ItemStack) obj}; + if (obj instanceof ItemStack[]) + return (ItemStack[]) obj; + if (obj instanceof List) + return ((List) obj).toArray(new ItemStack[0]); + + throw new ClassCastException("not an ItemStack, ItemStack[] or List getRange(final int start, final int end) { + return new AbstractList() + { + @Override + public Integer get(int index) { + return start+index; + } + + @Override + public int size() { + return end-start; + } + }; + } + + public static StringBuilder fixTrailingCommaList(StringBuilder sb) { + if(sb.charAt(sb.length()-1) == ',') + sb.deleteCharAt(sb.length()-1); + return sb; + } +} diff --git a/src/codechicken/nei/OffsetPositioner.java b/src/codechicken/nei/OffsetPositioner.java new file mode 100644 index 00000000..a76f9f3a --- /dev/null +++ b/src/codechicken/nei/OffsetPositioner.java @@ -0,0 +1,28 @@ +package codechicken.nei; + +import codechicken.nei.api.IStackPositioner; + +import java.util.ArrayList; + +public class OffsetPositioner implements IStackPositioner +{ + public OffsetPositioner(int x, int y) + { + offsetx = x; + offsety = y; + } + + @Override + public ArrayList positionStacks(ArrayList ai) + { + for(PositionedStack stack : ai) + { + stack.relx+=offsetx; + stack.rely+=offsety; + } + return ai; + } + + public int offsetx; + public int offsety; +} diff --git a/src/codechicken/nei/PlayerSave.java b/src/codechicken/nei/PlayerSave.java new file mode 100644 index 00000000..55e3fa69 --- /dev/null +++ b/src/codechicken/nei/PlayerSave.java @@ -0,0 +1,111 @@ +package codechicken.nei; + +import codechicken.core.ServerUtils; +import codechicken.lib.inventory.InventoryUtils; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; + +import java.io.File; + +public class PlayerSave +{ + public String username; + + private File saveFile; + private NBTTagCompound nbt; + + public ItemStack[] creativeInv; + private boolean creativeInvDirty; + + private boolean isDirty; + private boolean wasOp; + + public PlayerSave(String playername, File saveLocation) { + username = playername; + wasOp = ServerUtils.isPlayerOP(playername); + + saveFile = new File(saveLocation, username + ".dat"); + if (!saveFile.getParentFile().exists()) + saveFile.getParentFile().mkdirs(); + load(); + } + + private void load() { + nbt = new NBTTagCompound(); + try { + if (!saveFile.exists()) + saveFile.createNewFile(); + if (saveFile.length() > 0) + nbt = CompressedStreamTools.read(saveFile); + } catch (Exception e) { + e.printStackTrace(); + } + + loadCreativeInv(); + } + + private void loadCreativeInv() { + creativeInv = new ItemStack[54]; + NBTTagList itemList = nbt.getTagList("creativeitems", 10); + if (itemList != null) + InventoryUtils.readItemStacksFromTag(creativeInv, itemList); + } + + public void save() { + if (!isDirty) + return; + + if (creativeInvDirty) + saveCreativeInv(); + + try { + CompressedStreamTools.write(nbt, saveFile); + isDirty = false; + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void saveCreativeInv() { + NBTTagList invsave = InventoryUtils.writeItemStacksToTag(creativeInv); + nbt.setTag("creativeitems", invsave); + + creativeInvDirty = false; + } + + public void setCreativeDirty() { + creativeInvDirty = isDirty = true; + } + + public void setDirty() { + isDirty = true; + } + + public void updateOpChange(EntityPlayerMP player) { + boolean isOp = ServerUtils.isPlayerOP(username); + if (isOp != wasOp) { + NEISPH.sendHasServerSideTo(player); + wasOp = isOp; + } + } + + public boolean isActionEnabled(String name) { + return getEnabledActions().getBoolean(name); + } + + private NBTTagCompound getEnabledActions() { + NBTTagCompound tag = nbt.getCompoundTag("enabledActions"); + if (!nbt.hasKey("enabledActions")) + nbt.setTag("enabledActions", tag); + return tag; + } + + public void enableAction(String name, boolean enabled) { + getEnabledActions().setBoolean(name, enabled); + NEISPH.sendActionEnabled(ServerUtils.getPlayer(username), name, enabled); + setDirty(); + } +} diff --git a/src/codechicken/nei/PopupInputHandler.java b/src/codechicken/nei/PopupInputHandler.java new file mode 100644 index 00000000..fa8cd70f --- /dev/null +++ b/src/codechicken/nei/PopupInputHandler.java @@ -0,0 +1,69 @@ +package codechicken.nei; + +import codechicken.nei.guihook.IContainerInputHandler; +import net.minecraft.client.gui.inventory.GuiContainer; + +import static codechicken.nei.NEIClientConfig.canPerformAction; +import static codechicken.nei.NEIClientConfig.getKeyBinding; + +public class PopupInputHandler implements IContainerInputHandler +{ + @Override + public boolean keyTyped(GuiContainer gui, char keyChar, int keyCode) + { + return false; + } + + @Override + public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) + { + return false; + } + + @Override + public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) + { + } + + @Override + public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) + { + if(keyID == getKeyBinding("gui.enchant") && canPerformAction("enchant")) + { + NEICPH.sendOpenEnchantmentWindow(); + return true; + } + if(keyID == getKeyBinding("gui.potion") && canPerformAction("potion")) + { + NEICPH.sendOpenPotionWindow(); + return true; + } + return false; + } + + @Override + public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) + { + } + + @Override + public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) + { + } + + @Override + public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) + { + return false; + } + + @Override + public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) + { + } + + @Override + public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) + { + } +} diff --git a/src/codechicken/nei/PositionedStack.java b/src/codechicken/nei/PositionedStack.java new file mode 100644 index 00000000..dbaaf378 --- /dev/null +++ b/src/codechicken/nei/PositionedStack.java @@ -0,0 +1,119 @@ +package codechicken.nei; + +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; + +import java.util.ArrayList; +import java.util.List; + +/** + * Simply an {@link ItemStack} with position. + * Mainly used in the recipe handlers. + */ +public class PositionedStack +{ + public int relx; + public int rely; + public ItemStack items[]; + //compatibility dummy + public ItemStack item; + + private boolean permutated = false; + + public PositionedStack(Object object, int x, int y, boolean genPerms) + { + items = NEIServerUtils.extractRecipeItems(object); + relx = x; + rely = y; + + if(genPerms) + generatePermutations(); + else + setPermutationToRender(0); + } + + public PositionedStack(Object object, int x, int y) + { + this(object, x, y, true); + } + + public void generatePermutations() + { + if(permutated) + return; + + ArrayList stacks = new ArrayList(); + for(ItemStack item : items) + { + if(item == null || item.getItem() == null) + continue; + + if(item.getItemDamage() == Short.MAX_VALUE) + { + List permutations = ItemList.itemMap.get(item.getItem()); + if(!permutations.isEmpty()) + { + for(ItemStack stack : permutations) + stacks.add(stack.copy()); + } + else + { + ItemStack base = new ItemStack(item.getItem(), item.stackSize); + base.stackTagCompound = item.stackTagCompound; + stacks.add(base); + } + continue; + } + + stacks.add(item.copy()); + } + items = stacks.toArray(new ItemStack[0]); + + if(items.length == 0) + items = new ItemStack[]{new ItemStack(Blocks.fire)}; + + permutated = true; + setPermutationToRender(0); + } + + public void setMaxSize(int i) + { + for(ItemStack item : items) + if(item.stackSize > i) + item.stackSize = i; + } + + public PositionedStack copy() + { + return new PositionedStack(items, relx, rely); + } + + public void setPermutationToRender(int index) + { + item = items[index].copy(); + if(item.getItem() == null) + item = new ItemStack(Blocks.fire); + else if(item.getItemDamage() == OreDictionary.WILDCARD_VALUE) + item.setItemDamage(0); + } + + public boolean contains(ItemStack ingredient) + { + for(ItemStack item : items) + if(NEIServerUtils.areStacksSameTypeCrafting(item, ingredient)) + return true; + + return false; + } + + public boolean contains(Item ingred) + { + for(ItemStack item : items) + if(item.getItem() == ingred) + return true; + + return false; + } +} diff --git a/src/codechicken/nei/SaveLoadButton.java b/src/codechicken/nei/SaveLoadButton.java new file mode 100644 index 00000000..8a5daac5 --- /dev/null +++ b/src/codechicken/nei/SaveLoadButton.java @@ -0,0 +1,123 @@ +package codechicken.nei; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.util.ChatAllowedCharacters; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.input.Keyboard; + +import static codechicken.lib.gui.GuiDraw.drawString; +import static codechicken.lib.gui.GuiDraw.getStringWidth; +import static codechicken.nei.NEIClientUtils.translate; + +public abstract class SaveLoadButton extends Button +{ + public SaveLoadButton(String s) + { + super(s); + } + + @Override + public boolean handleClick(int mousex, int mousey, int button) + { + if(button == 1) + { + label = ""; + onTextChange(); + focused = true; + Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); + return true; + } + return super.handleClick(mousex, mousey, button); + } + + public abstract void onTextChange(); + + @Override + public void onGuiClick(int i, int j) + { + if(!contains(i, j)) + focused = false; + } + + @Override + public boolean handleKeyPress(int keyID, char keyChar) + { + if(!focused) + return false; + + if(keyID == Keyboard.KEY_BACK) + { + if(label.length() > 0) + { + label = label.substring(0, label.length() - 1); + onTextChange(); + backdowntime = System.currentTimeMillis(); + } + } + else if(keyID == Keyboard.KEY_RETURN) + { + focused = false; + } + else if(keyChar == 22)//paste + { + String pastestring = GuiScreen.getClipboardString(); + if(pastestring == null) + pastestring = ""; + + label = label + pastestring; + onTextChange(); + } + else if(ChatAllowedCharacters.isAllowedCharacter(keyChar)) + { + label = label + keyChar; + onTextChange(); + } + return true; + } + + @Override + public void update() + { + cursorCounter++; + if(backdowntime > 0) + { + if(Keyboard.isKeyDown(Keyboard.KEY_BACK) && label.length() > 5) + { + if(System.currentTimeMillis() - backdowntime > 200 / (1+backs * 0.3F)) + { + label = label.substring(0, label.length() - 1); + onTextChange(); + backdowntime = System.currentTimeMillis(); + backs++; + } + } + else + { + backdowntime = 0; + backs = 0; + } + } + } + + @Override + public void draw(int mousex, int mousey) + { + super.draw(mousex, mousey); + if(focused && (cursorCounter / 6) % 2 == 0) + drawString("_", x + (width + getStringWidth(getRenderLabel())) / 2, y + (height - 8) / 2, 0xFFFFFFFF); + } + + @Override + public String getRenderLabel() + { + return translate(saved ? "load" : "save") + " " + label; + } + + public boolean saved; + public long backdowntime; + public int backs; + public int cursorCounter; + public boolean focused; +} diff --git a/src/codechicken/nei/SearchField.java b/src/codechicken/nei/SearchField.java new file mode 100644 index 00000000..fd59a1ea --- /dev/null +++ b/src/codechicken/nei/SearchField.java @@ -0,0 +1,59 @@ +package codechicken.nei; + +import net.minecraft.util.EnumChatFormatting; + +import static codechicken.lib.gui.GuiDraw.drawGradientRect; +import static codechicken.lib.gui.GuiDraw.drawRect; +import static codechicken.nei.NEIClientConfig.world; + +public class SearchField extends TextField +{ + public SearchField(String ident) { + super(ident); + } + + public static boolean searchInventories() { + return world.nbt.getBoolean("searchinventories"); + } + + @Override + public void drawBox() { + if (searchInventories()) + drawGradientRect(x, y, width, height, 0xFFFFFF00, 0xFFC0B000); + else + drawRect(x, y, width, height, 0xffA0A0A0); + drawRect(x + 1, y + 1, width - 2, height - 2, 0xFF000000); + } + + @Override + public boolean handleClick(int mousex, int mousey, int button) { + if (button == 0) { + if (focused() && (System.currentTimeMillis() - lastclicktime < 500))//double click + { + world.nbt.setBoolean("searchinventories", !searchInventories()); + world.saveNBT(); + } + lastclicktime = System.currentTimeMillis(); + } + return super.handleClick(mousex, mousey, button); + } + + @Override + public void onTextChange(String oldText) { + NEIClientConfig.setSearchExpression(text()); + ItemList.updateSearch(); + } + + @Override + public void lastKeyTyped(int keyID, char keyChar) { + if (keyID == NEIClientConfig.getKeyBinding("gui.search")) + setFocus(true); + } + + @Override + public String filterText(String s) { + return EnumChatFormatting.getTextWithoutFormattingCodes(s); + } + + long lastclicktime; +} diff --git a/src/codechicken/nei/ServerHandler.java b/src/codechicken/nei/ServerHandler.java new file mode 100644 index 00000000..7ddc890b --- /dev/null +++ b/src/codechicken/nei/ServerHandler.java @@ -0,0 +1,149 @@ +package codechicken.nei; + +import codechicken.core.CommonUtils; +import codechicken.lib.packet.PacketCustom; +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerChangedDimensionEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerRespawnEvent; +import cpw.mods.fml.common.gameevent.TickEvent; +import cpw.mods.fml.common.gameevent.TickEvent.Phase; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.World; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.WorldEvent; + +import java.util.List; + +public class ServerHandler +{ + private static ServerHandler instance; + + public static void load() { + instance = new ServerHandler(); + + PacketCustom.assignHandler(NEICPH.channel, new NEISPH()); + FMLCommonHandler.instance().bus().register(instance); + MinecraftForge.EVENT_BUS.register(instance); + + NEIActions.init(); + } + + @SubscribeEvent + public void tickEvent(TickEvent.WorldTickEvent event) { + if (event.phase == Phase.START) + processDisabledProperties(event.world); + } + + @SubscribeEvent + public void loadEvent(WorldEvent.Load event) { + if(!event.world.isRemote) + NEIServerConfig.load(event.world); + } + + @SubscribeEvent + public void tickEvent(TickEvent.PlayerTickEvent event) { + if (event.phase == Phase.START && event.player instanceof EntityPlayerMP) { + EntityPlayerMP player = (EntityPlayerMP) event.player; + PlayerSave save = NEIServerConfig.forPlayer(player.getCommandSenderName()); + if (save == null) + return; + updateMagneticPlayer(player, save); + save.updateOpChange(player); + save.save(); + } + } + + private void processDisabledProperties(World world) { + NEIServerUtils.advanceDisabledTimes(world); + if (NEIServerUtils.isRaining(world) && NEIServerConfig.isActionDisabled(CommonUtils.getDimension(world), "rain")) + NEIServerUtils.toggleRaining(world, false); + } + + private void updateMagneticPlayer(EntityPlayerMP player, PlayerSave save) { + if (!save.isActionEnabled("magnet") || player.isDead) + return; + + float distancexz = 16; + float distancey = 8; + double maxspeedxz = 0.5; + double maxspeedy = 0.5; + double speedxz = 0.05; + double speedy = 0.07; + List items = player.worldObj.getEntitiesWithinAABB(EntityItem.class, player.boundingBox.expand(distancexz, distancey, distancexz)); + for (EntityItem item : items) { + if (item.delayBeforeCanPickup > 0) continue; + if (!NEIServerUtils.canItemFitInInventory(player, item.getEntityItem())) continue; + if (item.delayBeforeCanPickup == 0) { + NEISPH.sendAddMagneticItemTo(player, item); + } + + double dx = player.posX - item.posX; + double dy = player.posY + player.getEyeHeight() - item.posY; + double dz = player.posZ - item.posZ; + double absxz = Math.sqrt(dx * dx + dz * dz); + double absy = Math.abs(dy); + if (absxz > distancexz) { + continue; + } + if (absxz < 1) { + item.onCollideWithPlayer(player); + } + + if (absxz > 1) { + dx /= absxz; + dz /= absxz; + } + + if (absy > 1) { + dy /= absy; + } + + double vx = item.motionX + speedxz * dx; + double vy = item.motionY + speedy * dy; + double vz = item.motionZ + speedxz * dz; + + double absvxz = Math.sqrt(vx * vx + vz * vz); + double absvy = Math.abs(vy); + + double rationspeedxz = absvxz / maxspeedxz; + if (rationspeedxz > 1) { + vx /= rationspeedxz; + vz /= rationspeedxz; + } + + double rationspeedy = absvy / maxspeedy; + if (rationspeedy > 1) { + vy /= rationspeedy; + } + + item.motionX = vx; + item.motionY = vy; + item.motionZ = vz; + } + } + + @SubscribeEvent + public void loginEvent(PlayerLoggedInEvent event) { + NEIServerConfig.loadPlayer(event.player); + NEISPH.sendHasServerSideTo((EntityPlayerMP) event.player); + } + + @SubscribeEvent + public void logoutEvent(PlayerLoggedOutEvent event) { + NEIServerConfig.unloadPlayer(event.player); + } + + @SubscribeEvent + public void dimChangeEvent(PlayerChangedDimensionEvent event) { + NEISPH.sendHasServerSideTo((EntityPlayerMP) event.player); + } + + @SubscribeEvent + public void loginEvent(PlayerRespawnEvent event) { + NEISPH.sendHasServerSideTo((EntityPlayerMP) event.player); + } +} diff --git a/src/codechicken/nei/SlotOP.java b/src/codechicken/nei/SlotOP.java new file mode 100644 index 00000000..62030b45 --- /dev/null +++ b/src/codechicken/nei/SlotOP.java @@ -0,0 +1,10 @@ +package codechicken.nei; + +public abstract class SlotOP +{ + public abstract String getDescription(); + + public abstract int[] getKeyBind(); + + public abstract void operate(); +} diff --git a/src/codechicken/nei/SpawnerRenderer.java b/src/codechicken/nei/SpawnerRenderer.java new file mode 100644 index 00000000..79765567 --- /dev/null +++ b/src/codechicken/nei/SpawnerRenderer.java @@ -0,0 +1,97 @@ +package codechicken.nei; + +import codechicken.core.ClientUtils; +import codechicken.lib.render.TextureUtils; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.entity.Entity; +import net.minecraft.entity.boss.BossStatus; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.client.IItemRenderer; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +public class SpawnerRenderer implements IItemRenderer +{ + @Override + public boolean handleRenderType(ItemStack item, ItemRenderType type) + { + return true; + } + + public void renderInventoryItem(RenderBlocks render, ItemStack item) + { + int meta = item.getItemDamage(); + + if(meta == 0) + { + meta = ItemMobSpawner.idPig; + } + String bossName = BossStatus.bossName; + int bossTimeout = BossStatus.statusBarTime; + try + { + World world = NEIClientUtils.mc().theWorld; + ItemMobSpawner.loadSpawners(world); + TextureUtils.bindAtlas(0); + render.renderBlockAsItem(Blocks.mob_spawner, 0, 1F); + GL11.glPushMatrix(); + + Entity entity = ItemMobSpawner.getEntity(meta); + if(entity != null) + { + entity.setWorld(world); + float f1 = 0.4375F; + if(entity.getShadowSize() > 1.5) + { + f1 = 0.1F; + } + GL11.glRotatef((float) (ClientUtils.getRenderTime()*10), 0.0F, 1.0F, 0.0F); + GL11.glRotatef(-20F, 1.0F, 0.0F, 0.0F); + GL11.glTranslatef(0.0F, -0.4F, 0.0F); + GL11.glScalef(f1, f1, f1); + entity.setLocationAndAngles(0, 0, 0, 0.0F, 0.0F); + RenderManager.instance.renderEntityWithPosYaw(entity, 0.0D, 0.0D, 0.0D, 0.0F, 0); + } + GL11.glPopMatrix(); + + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); + GL11.glDisable(GL11.GL_TEXTURE_2D); + OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + } + catch(Exception e) + { + if(Tessellator.instance.isDrawing) + Tessellator.instance.draw(); + } + BossStatus.bossName = bossName; + BossStatus.statusBarTime = bossTimeout; + } + + @SuppressWarnings("incomplete-switch") + @Override + public void renderItem(ItemRenderType type, ItemStack item, Object... data) + { + switch(type) + { + case EQUIPPED: + case EQUIPPED_FIRST_PERSON: + GL11.glTranslatef(0.5F, 0.5F, 0.5F); + case INVENTORY: + case ENTITY: + renderInventoryItem((RenderBlocks)data[0], item); + break; + } + } + + @Override + public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) + { + return true; + } +} diff --git a/src/codechicken/nei/TMIUninstaller.java b/src/codechicken/nei/TMIUninstaller.java new file mode 100644 index 00000000..c298f1bc --- /dev/null +++ b/src/codechicken/nei/TMIUninstaller.java @@ -0,0 +1,279 @@ +package codechicken.nei; + +import net.minecraft.client.Minecraft; + +import javax.swing.*; +import java.awt.*; +import java.awt.LayoutManager; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.*; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +public class TMIUninstaller +{ + public static class InstallerGui extends JFrame + { + private static final long serialVersionUID = 1L; + private JTextField labelInfo; + private static final String PREFERRED_LOOK_AND_FEEL = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; + + public InstallerGui() { + setTitle("TMI Uninstaller"); + initComponents(); + } + + private class ResizeListener implements LayoutManager + { + public void addLayoutComponent(String name, Component comp) { + } + + public void layoutContainer(Container parent) { + int w = getContentPane().getWidth(); + labelInfo.setBounds(10, 45, w - 20, 20); + labelInfo.setScrollOffset(1000); + labelInfo.update(getGraphics()); + } + + public Dimension minimumLayoutSize(Container parent) { + return new Dimension(150, 100); + } + + public Dimension preferredLayoutSize(Container parent) { + return new Dimension(250, 120); + } + + + public void removeLayoutComponent(Component comp) { + } + } + + private void initComponents() { + setLayout(new ResizeListener()); + add(getLabelInfo()); + setSize(358, 270); + } + + public class InstallerListener implements ActionListener + { + public void actionPerformed(ActionEvent e) { + } + } + + public JTextField getLabelInfo() { + if (labelInfo == null) { + labelInfo = new JTextField(); + labelInfo.setFont(new Font("Tahoma", Font.PLAIN, 13)); + labelInfo.setHorizontalAlignment(SwingConstants.CENTER); + labelInfo.setEditable(false); + labelInfo.setText("Uninstalling TMI"); + } + return labelInfo; + } + + public static void installLnF() { + try { + String lnfClassname = UIManager.getCrossPlatformLookAndFeelClassName(); + UIManager.setLookAndFeel(lnfClassname); + } catch (Exception e) { + System.err.println("Cannot install " + PREFERRED_LOOK_AND_FEEL + " on this platform:" + e.getMessage()); + } + } + } + + private static File jarFile; + public static InstallerGui mainframe; + + public static void main(String[] args) { + jarFile = new File(args[0]); + + InstallerGui.installLnF(); + mainframe = new InstallerGui(); + mainframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + mainframe.setLocationRelativeTo(null); + mainframe.pack(); + mainframe.setVisible(true); + + if (!jarFile.exists()) + mainframe.getLabelInfo().setText("Invalid Minecraft.jar"); + else + uninstall(); + } + + private static File getJarFile() { + URL url = Minecraft.class.getProtectionDomain().getCodeSource().getLocation(); + try { + if (url.getProtocol().equals("jar")) + url = new URL(url.getPath().substring(0, url.getPath().indexOf('!'))); + return new File(url.toURI()); + } catch (URISyntaxException e) { + return new File(url.getPath()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + public static void deleteTMIUninstaller() throws IOException { + File file = new File(getJarFile().getParentFile(), "TMIUninstaller"); + if (file.exists()) { + NEIClientConfig.logger.info("Removing TMI Uninstaller"); + deleteDir(file, true); + } + } + + public static boolean TMIInstalled() { + File jarFile = getJarFile(); + if (!jarFile.getName().endsWith(".jar")) + return false; + + try { + ZipFile zip = new ZipFile(jarFile); + ZipEntry tmi = zip.getEntry("mod_TooManyItems.class"); + zip.close(); + return tmi != null; + } catch (Exception e) { + return false; + } + } + + public static void runTMIUninstaller() throws IOException { + File jarFile = getJarFile(); + File uninstDir = new File(jarFile.getParentFile(), "TMIUninstaller"); + if (!uninstDir.exists()) + uninstDir.mkdirs(); + + NEIClientConfig.logger.info("Installing Uninstaller: " + uninstDir.getPath()); + + FileInputStream fileinputstream = new FileInputStream(jarFile); + ZipInputStream zipinputstream = new ZipInputStream(fileinputstream); + do { + ZipEntry zipentry = zipinputstream.getNextEntry(); + if (zipentry == null) { + break; + } + String fullname = zipentry.getName().replace('\\', '/'); + if (!zipentry.isDirectory() && fullname.replace('/', '.').startsWith(TMIUninstaller.class.getCanonicalName())) { + File outFile = new File(uninstDir, fullname); + NEIClientConfig.logger.info("Extracting File: " + outFile.getPath()); + + if (!outFile.getParentFile().exists()) + outFile.getParentFile().mkdirs(); + + outFile.createNewFile(); + FileOutputStream fout = new FileOutputStream(outFile); + + byte[] buffer = new byte[65535]; + int read = 0; + while ((read = zipinputstream.read(buffer)) != -1)//write loop + { + fout.write(buffer, 0, read); + } + fout.close(); + } + } + while (true); + fileinputstream.close(); + + String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; + + String classpath = uninstDir.getAbsolutePath(); + String className = TMIUninstaller.class.getCanonicalName(); + + String jarPath = jarFile.getPath(); + + NEIClientConfig.logger.info("Running Process: " + javaBin + " -cp \"" + classpath + "\" \"" + className + "\" \"" + jarPath + "\""); + + ProcessBuilder builder = new ProcessBuilder(javaBin, "-cp", classpath, className, jarPath); + builder.start(); + } + + public static void deleteDir(File directory, boolean remove) throws IOException { + NEIClientConfig.logger.debug("Deleting Dir: " + directory.getPath()); + if (!directory.exists()) { + if (!remove) + directory.mkdirs(); + + return; + } + for (File file : directory.listFiles()) { + if (file.isDirectory()) { + deleteDir(file, true); + } else { + if (!file.delete()) { + throw new IOException("Delete Failed: " + file); + } + } + } + if (remove) { + try { + if (!directory.delete()) { + throw new IOException("Delete Failed: " + directory); + } + } catch (SecurityException e) { + throw e; + } + } + } + + public static void uninstall() { + File backupfile = new File(jarFile.getParentFile(), jarFile.getName() + ".bak"); + try { + if (!backupfile.exists()) + backupfile.createNewFile(); + + FileOutputStream backout = new FileOutputStream(backupfile); + FileInputStream jarin = new FileInputStream(jarFile); + backout.getChannel().transferFrom(jarin.getChannel(), 0, jarFile.length()); + + backout.close(); + jarin.close(); + + ZipInputStream zipin = new ZipInputStream(new FileInputStream(backupfile)); + ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(jarFile)); + byte[] buffer = new byte[20000]; + int read = 0; + while (true) { + ZipEntry entry = zipin.getNextEntry(); + if (entry == null) { + break; + } + if (entry.isDirectory()) continue; + String name = entry.getName(); + if (name.startsWith("TMI") || name.startsWith("_tmi") || name.startsWith("tmi.png") || name.startsWith("mod_TooManyItems")) { + continue; + } + zipout.putNextEntry(new ZipEntry(name)); + InputStream in = zipin; + while ((read = in.read(buffer)) != -1) { + zipout.write(buffer, 0, read); + } + zipout.closeEntry(); + zipin.closeEntry(); + } + zipin.close(); + zipout.close(); + + mainframe.getLabelInfo().setText("Uninstall Completed. Close this and restart minecraft."); + } catch (Exception e) { + e.printStackTrace(); + mainframe.getLabelInfo().setText("Invalid Minecraft.jar"); + try { + FileInputStream backin = new FileInputStream(backupfile); + FileOutputStream jarout = new FileOutputStream(jarFile); + jarout.getChannel().transferFrom(backin.getChannel(), 0, backupfile.length()); + + backin.close(); + jarout.close(); + } catch (Exception e1) { + e1.printStackTrace(); + } + } + } +} diff --git a/src/codechicken/nei/TextField.java b/src/codechicken/nei/TextField.java new file mode 100644 index 00000000..2bf4e627 --- /dev/null +++ b/src/codechicken/nei/TextField.java @@ -0,0 +1,182 @@ +package codechicken.nei; + +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.util.ChatAllowedCharacters; +import org.lwjgl.input.Keyboard; + +import static codechicken.lib.gui.GuiDraw.*; + +public abstract class TextField extends Widget +{ + public TextField(String ident) + { + identifier = ident; + } + + public int getTextColour() + { + return focused() ? 0xFFE0E0E0 : 0xFF909090; + } + + public void drawBox() + { + drawRect(x, y, width, height, 0xffA0A0A0); + drawRect(x + 1, y + 1, width - 2, height - 2, 0xFF000000); + } + + @Override + public void draw(int mousex, int mousey) + { + drawBox(); + + String drawtext = text; + + if(text.length() > getMaxTextLength()) + { + int startOffset = drawtext.length() - getMaxTextLength(); + if(startOffset < 0 || startOffset > drawtext.length()) + startOffset = 0; + drawtext = drawtext.substring(startOffset); + } + + if(focused() && (cursorCounter / 6) % 2 == 0) + drawtext = drawtext + '_'; + + int textWidth = getStringWidth(text); + int textx = centered ? x+(width-textWidth)/2 : x + 4; + int texty = y + (height + 1) / 2 - 3; + + drawString(drawtext, textx, texty, getTextColour()); + } + + @Override + public void onGuiClick(int mousex, int mousey) + { + if(!contains(mousex, mousey)) + setFocus(false); + } + + @Override + public boolean handleClick(int mousex, int mousey, int button) + { + if(button == 1) + setText(""); + setFocus(true); + return true; + } + + @Override + public boolean handleKeyPress(int keyID, char keyChar) + { + if(!focused()) + return false; + + if(keyID == Keyboard.KEY_BACK) + { + if(text.length() > 0) + { + setText(text.substring(0, text.length() - 1)); + backdowntime = System.currentTimeMillis(); + } + } + else if(keyID == Keyboard.KEY_RETURN || keyID == Keyboard.KEY_ESCAPE) + { + setFocus(false); + onExit(); + } + else if(keyChar == 22)//paste + { + String pastestring = GuiScreen.getClipboardString(); + if(pastestring == null) + pastestring = ""; + + if(isValid(text + pastestring)) + setText(text + pastestring); + } + else if(isValid(text+keyChar)) + setText(text+keyChar); + + return true; + } + + public void onExit() + { + } + + public abstract void onTextChange(String oldText); + + public boolean isValid(String string) + { + // Solve the problem that Minecraft can't post Chinese characters + return ChatAllowedCharacters.isAllowedCharacter(string.charAt(string.length()-1)); + } + + @Override + public void update() + { + cursorCounter++; + if(backdowntime > 0) + { + if(Keyboard.isKeyDown(Keyboard.KEY_BACK) && text.length() > 0) + { + if(System.currentTimeMillis() - backdowntime > 200 / (1+backs * 0.3F)) + { + setText(text.substring(0, text.length() - 1)); + backdowntime = System.currentTimeMillis(); + backs++; + } + } + else + { + backdowntime = 0; + backs = 0; + } + } + } + + public void setText(String s) + { + String oldText = text; + text = filterText(s); + onTextChange(oldText); + } + + public String filterText(String s) + { + return s; + } + + private int getMaxTextLength() + { + return width / 6 - 2; + } + + public void setFocus(boolean focus) + { + if(focus) + { + LayoutManager.setInputFocused(this); + } + else if(focused()) + { + LayoutManager.setInputFocused(null); + } + } + + public boolean focused() + { + return LayoutManager.getInputFocused() == this; + } + + public String text() + { + return text; + } + + private String text = ""; + public boolean centered; + public long backdowntime; + public int backs; + public String identifier; + public int cursorCounter; +} diff --git a/src/codechicken/nei/VisiblityData.java b/src/codechicken/nei/VisiblityData.java new file mode 100644 index 00000000..f5753aa8 --- /dev/null +++ b/src/codechicken/nei/VisiblityData.java @@ -0,0 +1,38 @@ +package codechicken.nei; + +public class VisiblityData +{ + public boolean showUtilityButtons = true; + public boolean showStateButtons = true; + /** + * Item panel and associated buttons + */ + public boolean showItemPanel = true; + /** + * Item and search section + */ + public boolean showItemSection = true; + /** + * Dropdown and Item search field + */ + public boolean showSearchSection = true; + /** + * All widgets except options button + */ + public boolean showWidgets = true; + /** + * The entire NEI interface, aka hidden + */ + public boolean showNEI = true; + public boolean enableDeleteMode = true; + + public void translateDependancies() + { + if(!showNEI) + showWidgets = false; + if(!showWidgets) + showItemSection = showUtilityButtons = showStateButtons = false; + if(!showItemSection) + showSearchSection = showItemPanel = false; + } +} diff --git a/src/codechicken/nei/Widget.java b/src/codechicken/nei/Widget.java new file mode 100644 index 00000000..77a56910 --- /dev/null +++ b/src/codechicken/nei/Widget.java @@ -0,0 +1,72 @@ +package codechicken.nei; + +import net.minecraft.item.ItemStack; + +import java.util.List; + +public abstract class Widget +{ + public Widget() { + } + + public abstract void draw(int mousex, int mousey); + + public void postDraw(int mousex, int mousey) { + } + + public boolean handleClick(int mousex, int mousey, int button) { + return true; + } + + public void onGuiClick(int mousex, int mousey) { + } + + public void mouseUp(int mousex, int mousey, int button) { + + } + + public boolean handleKeyPress(int keyID, char keyChar) { + return false; + } + + public void lastKeyTyped(int keyID, char keyChar) { + } + + public boolean handleClickExt(int mousex, int mousey, int button) { + return false; + } + + public boolean onMouseWheel(int i, int mousex, int mousey) { + return false; + } + + public void update() { + } + + public boolean contains(int posx, int posy) { + return posx >= x && posx < x + width && posy >= y && posy < y + height; + } + + public ItemStack getStackMouseOver(int mousex, int mousey) { + return null; + } + + public void mouseDragged(int mx, int my, int button, long heldTime) { + } + + public List handleTooltip(int mx, int my, List tooltip) { + return tooltip; + } + + public void loseFocus() { + } + + public void gainFocus() { + } + + public int x; + public int y; + public int z; + public int width; + public int height; +} diff --git a/src/codechicken/nei/WidgetZOrder.java b/src/codechicken/nei/WidgetZOrder.java new file mode 100644 index 00000000..e6679f01 --- /dev/null +++ b/src/codechicken/nei/WidgetZOrder.java @@ -0,0 +1,18 @@ +package codechicken.nei; + +import java.util.Comparator; + +public class WidgetZOrder + implements Comparator +{ + boolean topfirst; + public WidgetZOrder(boolean topfirst) + { + this.topfirst = topfirst; + } + + public int compare(Widget w1, Widget w2) + { + return w1.z != w2.z ? ((topfirst ? w1.z > w2.z : w1.z < w2.z) ? 1 : -1) : 1; + } +} diff --git a/src/codechicken/nei/WorldOverlayRenderer.java b/src/codechicken/nei/WorldOverlayRenderer.java new file mode 100644 index 00000000..2bac5899 --- /dev/null +++ b/src/codechicken/nei/WorldOverlayRenderer.java @@ -0,0 +1,222 @@ +package codechicken.nei; + +import codechicken.lib.math.MathHelper; +import codechicken.lib.render.RenderUtils; +import codechicken.nei.KeyManager.IKeyStateTracker; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.entity.passive.EntityPig; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.EnumSkyBlock; +import net.minecraft.world.SpawnerAnimals; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraft.world.chunk.Chunk; +import org.lwjgl.opengl.GL11; + +public class WorldOverlayRenderer implements IKeyStateTracker +{ + public static int mobOverlay = 0; + public static int chunkOverlay = 0; + + public static void reset() { + mobOverlay = 0; + chunkOverlay = 0; + } + + @Override + public void tickKeyStates() { + if (Minecraft.getMinecraft().currentScreen != null) + return; + + if (KeyManager.keyStates.get("world.moboverlay").down) + mobOverlay = (mobOverlay + 1) % 2; + if (KeyManager.keyStates.get("world.chunkoverlay").down) + chunkOverlay = (chunkOverlay + 1) % 3; + } + + public static void render(float frame) { + GL11.glPushMatrix(); + Entity entity = Minecraft.getMinecraft().renderViewEntity; + RenderUtils.translateToWorldCoords(entity, frame); + + renderChunkBounds(entity); + renderMobSpawnOverlay(entity); + GL11.glPopMatrix(); + } + + private static void renderMobSpawnOverlay(Entity entity) { + if (mobOverlay == 0) + return; + + GL11.glDisable(GL11.GL_TEXTURE_2D); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glLineWidth(1.5F); + GL11.glBegin(GL11.GL_LINES); + + GL11.glColor4f(1, 0, 0, 1); + int curSpawnMode = 2; + + World world = entity.worldObj; + int x1 = (int) entity.posX; + int z1 = (int) entity.posZ; + int y1 = (int) MathHelper.clip(entity.posY, 16, world.getHeight() - 16); + + for (int x = x1 - 16; x <= x1 + 16; x++) + for (int z = z1 - 16; z <= z1 + 16; z++) { + Chunk chunk = world.getChunkFromBlockCoords(x, z); + BiomeGenBase biome = world.getBiomeGenForCoords(x, z); + if (biome.getSpawnableList(EnumCreatureType.monster).isEmpty() || biome.getSpawningChance() <= 0) + continue; + + for (int y = y1 - 16; y < y1 + 16; y++) { + int spawnMode = getSpawnMode(chunk, x, y, z); + if (spawnMode == 0) + continue; + + if (spawnMode != curSpawnMode) { + if (spawnMode == 1) + GL11.glColor4f(1, 1, 0, 1); + else + GL11.glColor4f(1, 0, 0, 1); + curSpawnMode = spawnMode; + } + + GL11.glVertex3d(x, y + 0.004, z); + GL11.glVertex3d(x + 1, y + 0.004, z + 1); + GL11.glVertex3d(x + 1, y + 0.004, z); + GL11.glVertex3d(x, y + 0.004, z + 1); + } + } + + GL11.glEnd(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_TEXTURE_2D); + GL11.glDisable(GL11.GL_BLEND); + } + + private static Entity dummyEntity = new EntityPig(null); + private static AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(0, 0, 0, 0, 0, 0); + private static int getSpawnMode(Chunk chunk, int x, int y, int z) { + if (!SpawnerAnimals.canCreatureTypeSpawnAtLocation(EnumCreatureType.monster, chunk.worldObj, x, y, z) || + chunk.getSavedLightValue(EnumSkyBlock.Block, x & 15, y, z & 15) >= 8) + return 0; + + aabb.minX = x + 0.2; + aabb.maxX = x + 0.8; + aabb.minY = y + 0.01; + aabb.maxY = y + 1.8; + aabb.minZ = z + 0.2; + aabb.maxZ = z + 0.8; + if (!chunk.worldObj.checkNoEntityCollision(aabb) || + !chunk.worldObj.getCollidingBoundingBoxes(dummyEntity, aabb).isEmpty() || + chunk.worldObj.isAnyLiquid(aabb)) + return 0; + + if (chunk.getSavedLightValue(EnumSkyBlock.Sky, x & 15, y, z & 15) >= 8) + return 1; + return 2; + } + + private static void renderChunkBounds(Entity entity) { + if (chunkOverlay == 0) + return; + + GL11.glDisable(GL11.GL_TEXTURE_2D); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glLineWidth(1.5F); + GL11.glBegin(GL11.GL_LINES); + + for (int cx = -4; cx <= 4; cx++) + for (int cz = -4; cz <= 4; cz++) { + double x1 = (entity.chunkCoordX + cx) << 4; + double z1 = (entity.chunkCoordZ + cz) << 4; + double x2 = x1 + 16; + double z2 = z1 + 16; + + double dy = 128; + double y1 = Math.floor(entity.posY - dy / 2); + double y2 = y1 + dy; + if (y1 < 0) { + y1 = 0; + y2 = dy; + } + + if (y1 > entity.worldObj.getHeight()) { + y2 = entity.worldObj.getHeight(); + y1 = y2 - dy; + } + + double dist = Math.pow(1.5, -(cx * cx + cz * cz)); + + GL11.glColor4d(0.9, 0, 0, dist); + if (cx >= 0 && cz >= 0) { + GL11.glVertex3d(x2, y1, z2); + GL11.glVertex3d(x2, y2, z2); + } + if (cx >= 0 && cz <= 0) { + GL11.glVertex3d(x2, y1, z1); + GL11.glVertex3d(x2, y2, z1); + } + if (cx <= 0 && cz >= 0) { + GL11.glVertex3d(x1, y1, z2); + GL11.glVertex3d(x1, y2, z2); + } + if (cx <= 0 && cz <= 0) { + GL11.glVertex3d(x1, y1, z1); + GL11.glVertex3d(x1, y2, z1); + } + + if (chunkOverlay == 2 && cx == 0 && cz == 0) { + dy = 32; + y1 = Math.floor(entity.posY - dy / 2); + y2 = y1 + dy; + if (y1 < 0) { + y1 = 0; + y2 = dy; + } + + if (y1 > entity.worldObj.getHeight()) { + y2 = entity.worldObj.getHeight(); + y1 = y2 - dy; + } + + GL11.glColor4d(0, 0.9, 0, 0.4); + for (double y = (int) y1; y <= y2; y++) { + GL11.glVertex3d(x2, y, z1); + GL11.glVertex3d(x2, y, z2); + GL11.glVertex3d(x1, y, z1); + GL11.glVertex3d(x1, y, z2); + GL11.glVertex3d(x1, y, z2); + GL11.glVertex3d(x2, y, z2); + GL11.glVertex3d(x1, y, z1); + GL11.glVertex3d(x2, y, z1); + } + for (double h = 1; h <= 15; h++) { + GL11.glVertex3d(x1 + h, y1, z1); + GL11.glVertex3d(x1 + h, y2, z1); + GL11.glVertex3d(x1 + h, y1, z2); + GL11.glVertex3d(x1 + h, y2, z2); + GL11.glVertex3d(x1, y1, z1 + h); + GL11.glVertex3d(x1, y2, z1 + h); + GL11.glVertex3d(x2, y1, z1 + h); + GL11.glVertex3d(x2, y2, z1 + h); + } + } + } + + GL11.glEnd(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_TEXTURE_2D); + GL11.glDisable(GL11.GL_BLEND); + } + + public static void load() { + KeyManager.trackers.add(new WorldOverlayRenderer()); + } +} diff --git a/src/codechicken/nei/api/API.java b/src/codechicken/nei/api/API.java new file mode 100644 index 00000000..c26815ea --- /dev/null +++ b/src/codechicken/nei/api/API.java @@ -0,0 +1,195 @@ +package codechicken.nei.api; + +import codechicken.nei.KeyManager; +import codechicken.nei.KeyManager.KeyState; +import codechicken.nei.LayoutManager; +import codechicken.nei.NEIClientConfig; +import codechicken.nei.OffsetPositioner; +import codechicken.nei.config.Option; +import codechicken.nei.config.OptionKeyBind; +import codechicken.nei.recipe.*; +import net.minecraft.block.Block; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import org.lwjgl.input.Keyboard; + +/** + * This is the main class that handles item property configuration. + * WARNING: DO NOT access this class until the world has been loaded + * These methods should be called from INEIConfig implementors + */ +public class API +{ + /** + * Register a new Crafting Recipe handler; + * + * @param handler The handler to register + */ + public static void registerRecipeHandler(ICraftingHandler handler) { + GuiCraftingRecipe.registerRecipeHandler(handler); + } + + /** + * Register a new Usage Recipe handler; + * + * @param handler The handler to register + */ + public static void registerUsageHandler(IUsageHandler handler) { + GuiUsageRecipe.registerUsageHandler(handler); + } + + /** + * Add a gui to the default overlay renderer with the default position + * + * @param classz The class of your gui + * @param ident The identification string, currently + * {crafting, crafting2x2, smelting, fuel, brewing} + */ + public static void registerGuiOverlay(Class classz, String ident) { + registerGuiOverlay(classz, ident, 5, 11); + } + + /** + * Add a gui to the default overlay renderer with an offset + * + * @param classz The class of your gui + * @param ident The identification string, currently + * {crafting, crafting2x2, smelting, fuel, brewing} + * @param x x-offset + * @param y y-offset + */ + public static void registerGuiOverlay(Class classz, String ident, int x, int y) { + registerGuiOverlay(classz, ident, new OffsetPositioner(x, y)); + } + + /** + * Add a gui to the default overlay renderer + * + * @param classz The class of your gui + * @param ident The identification string, currently + * {crafting, crafting2x2, smelting, fuel, brewing} + * @param positioner A Stack Repositioner for moving the items to the right place + */ + public static void registerGuiOverlay(Class classz, String ident, IStackPositioner positioner) { + RecipeInfo.registerGuiOverlay(classz, ident, positioner); + } + + /** + * @param classz The class of your gui + * @param handler The handler to register + * @param ident The recipe identification string + */ + public static void registerGuiOverlayHandler(Class classz, IOverlayHandler handler, String ident) { + RecipeInfo.registerOverlayHandler(classz, handler, ident); + } + + /** + * Set the offset to be added to items to translate them into recipe coords on the actual gui, default is 5, 11. Primarily RecipeTransferRects + * + * @param classz The class of your gui + * @param x + * @param y + */ + public static void setGuiOffset(Class classz, int x, int y) { + RecipeInfo.setGuiOffset(classz, x, y); + } + + public static void registerNEIGuiHandler(INEIGuiHandler handler) { + GuiInfo.guiHandlers.add(handler); + } + + /** + * Hide an item from the item panel + * Damage values of OreDictionary.WILDCARD_VALUE and null NBT tags function as wildcards for their respective variables + */ + public static void hideItem(ItemStack item) { + ItemInfo.hiddenItems.add(item); + } + + /** + * Add or replace the name normally shown on the item tooltip + */ + public static void setOverrideName(ItemStack item, String name) { + ItemInfo.nameOverrides.put(item, name); + } + + /** + * Adds an item to the item panel. Any items added using this function will override the default search pattern. + * @param item an item with data + */ + public static void addItemListEntry(ItemStack item) { + ItemInfo.itemOverrides.put(item.getItem(), item); + } + + /** + * Add a custom KeyBinding to be configured in the Controls menu. + * @param ident An identifier for your key, eg "shoot" + * @param defaultKey The default value, commonly obtained from {@link Keyboard} + */ + public static void addKeyBind(String ident, int defaultKey) { + NEIClientConfig.setDefaultKeyBinding(ident, defaultKey); + KeyManager.keyStates.put(ident, new KeyState()); + addOption(new OptionKeyBind(ident)); + } + + public static void addOption(Option option) { + NEIClientConfig.getOptionList().addOption(option); + } + + /** + * Add a new Layout Style for the NEI interface + * @param styleID The Unique ID to be used for storing your style in the config and cycling through avaliable styles + * @param style The style to add. + */ + public static void addLayoutStyle(int styleID, LayoutStyle style) { + LayoutManager.layoutStyles.put(styleID, style); + } + + /** + * Registers a new Infinite Item Handler. + * + * @param handler The handler to be registered. + */ + public static void addInfiniteItemHandler(IInfiniteItemHandler handler) { + ItemInfo.infiniteHandlers.addFirst(handler); + } + + /** + * Registers a new Infinite Item Handler. + * + * @param block The block to handle, null for all. + * @param handler The handler to be registered. + */ + public static void registerHighlightIdentifier(Block block, IHighlightHandler handler) { + ItemInfo.highlightIdentifiers.put(block, handler); + } + + /** + * Tells NEI not to perform any Fast Transfer operations on slots of a particular class + * + * @param slotClass The class of slot to be exempted + */ + public static void addFastTransferExemptSlot(Class slotClass) { + ItemInfo.fastTransferExemptions.add(slotClass); + } + + /** + * Register a new text handler for the block highlight tooltip with a layout specification (HEADER, BODY or FOOTER). + * + * @param handler + * @param layout A HUDAugmenterRegistry.Layout entry. HEADER is displayed before BODY which is displayed before FOOTER. + */ + public static void registerHighlightHandler(IHighlightHandler handler, ItemInfo.Layout... layout) { + ItemInfo.registerHighlightHandler(handler, layout); + } + + /** + * Register a mode handler for overriding NEI recipe/utility/cheat mode settings. + * + * @param handler + */ + public static void registerModeHandler(INEIModeHandler handler) { + NEIInfo.modeHandlers.add(handler); + } +} diff --git a/src/codechicken/nei/api/DefaultOverlayRenderer.java b/src/codechicken/nei/api/DefaultOverlayRenderer.java new file mode 100644 index 00000000..894e2efe --- /dev/null +++ b/src/codechicken/nei/api/DefaultOverlayRenderer.java @@ -0,0 +1,44 @@ +package codechicken.nei.api; + +import codechicken.nei.PositionedStack; +import codechicken.nei.guihook.GuiContainerManager; +import net.minecraft.inventory.Slot; +import org.lwjgl.opengl.GL11; + +import java.util.ArrayList; +import java.util.List; + +public class DefaultOverlayRenderer implements IRecipeOverlayRenderer +{ + public DefaultOverlayRenderer(List ai, IStackPositioner positioner) + { + positioner = this.positioner = positioner; + ingreds = new ArrayList(); + for(PositionedStack stack : ai) + ingreds.add(stack.copy()); + ingreds = positioner.positionStacks(ingreds); + } + + @Override + public void renderOverlay(GuiContainerManager gui, Slot slot) + { + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(770, 1); + GL11.glColor4d(0.6, 0.6, 0.6, 0.7); + + GuiContainerManager.setColouredItemRender(true); + for(PositionedStack stack : ingreds) + { + if(stack.relx == slot.xDisplayPosition && stack.rely == slot.yDisplayPosition) + GuiContainerManager.drawItem(stack.relx, stack.rely, stack.item); + } + GuiContainerManager.setColouredItemRender(false); + + GL11.glColor4f(1, 1, 1, 1); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); + } + + IStackPositioner positioner; + ArrayList ingreds; +} diff --git a/src/codechicken/nei/api/GuiInfo.java b/src/codechicken/nei/api/GuiInfo.java new file mode 100644 index 00000000..11f71ee5 --- /dev/null +++ b/src/codechicken/nei/api/GuiInfo.java @@ -0,0 +1,26 @@ +package codechicken.nei.api; + +import codechicken.nei.NEIChestGuiHandler; +import codechicken.nei.NEICreativeGuiHandler; +import codechicken.nei.NEIDummySlotHandler; +import net.minecraft.client.gui.inventory.GuiContainer; + +import java.util.Iterator; +import java.util.LinkedList; + +public class GuiInfo +{ + public static LinkedList guiHandlers = new LinkedList(); + + public static void load() { + API.registerNEIGuiHandler(new NEICreativeGuiHandler()); + API.registerNEIGuiHandler(new NEIChestGuiHandler()); + API.registerNEIGuiHandler(new NEIDummySlotHandler()); + } + + public static void clearGuiHandlers() { + for (Iterator iterator = guiHandlers.iterator(); iterator.hasNext(); ) + if (iterator.next() instanceof GuiContainer) + iterator.remove(); + } +} diff --git a/src/codechicken/nei/api/IConfigureNEI.java b/src/codechicken/nei/api/IConfigureNEI.java new file mode 100644 index 00000000..598d5ee3 --- /dev/null +++ b/src/codechicken/nei/api/IConfigureNEI.java @@ -0,0 +1,13 @@ +package codechicken.nei.api; + +/** + * An nei configuration entry point should implement this class and have name "NEIConfig" + * loadConfig will only be called when NEI is installed. + */ +public interface IConfigureNEI +{ + public void loadConfig(); + + public String getName(); + public String getVersion(); +} diff --git a/src/codechicken/nei/api/IGuiContainerOverlay.java b/src/codechicken/nei/api/IGuiContainerOverlay.java new file mode 100644 index 00000000..73cc6322 --- /dev/null +++ b/src/codechicken/nei/api/IGuiContainerOverlay.java @@ -0,0 +1,8 @@ +package codechicken.nei.api; + +import net.minecraft.client.gui.inventory.GuiContainer; + +public interface IGuiContainerOverlay +{ + public GuiContainer getFirstScreen(); +} diff --git a/src/codechicken/nei/api/IHighlightHandler.java b/src/codechicken/nei/api/IHighlightHandler.java new file mode 100644 index 00000000..15fdb02d --- /dev/null +++ b/src/codechicken/nei/api/IHighlightHandler.java @@ -0,0 +1,15 @@ +package codechicken.nei.api; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.world.World; + +import java.util.List; + +public interface IHighlightHandler +{ + public ItemStack identifyHighlight(World world, EntityPlayer player, MovingObjectPosition mop); + + public List handleTextData(ItemStack itemStack, World world, EntityPlayer player, MovingObjectPosition mop, List currenttip, ItemInfo.Layout layout); +} diff --git a/src/codechicken/nei/api/IInfiniteItemHandler.java b/src/codechicken/nei/api/IInfiniteItemHandler.java new file mode 100644 index 00000000..bae6a831 --- /dev/null +++ b/src/codechicken/nei/api/IInfiniteItemHandler.java @@ -0,0 +1,16 @@ +package codechicken.nei.api; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; + +public interface IInfiniteItemHandler +{ + void onPickup(ItemStack heldItem); + void onPlaceInfinite(ItemStack heldItem); + + boolean canHandleItem(ItemStack stack); + boolean isItemInfinite(ItemStack stack); + + public void replenishInfiniteStack(InventoryPlayer inventory, int slotNo); + public ItemStack getInfiniteItem(ItemStack typeStack); +} diff --git a/src/codechicken/nei/api/INEIGuiAdapter.java b/src/codechicken/nei/api/INEIGuiAdapter.java new file mode 100644 index 00000000..6ee6413a --- /dev/null +++ b/src/codechicken/nei/api/INEIGuiAdapter.java @@ -0,0 +1,43 @@ +package codechicken.nei.api; + +import codechicken.nei.VisiblityData; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; + +import java.util.Collections; +import java.util.List; + +/** + * Lets you just override those things you want to + */ +public class INEIGuiAdapter implements INEIGuiHandler +{ + @Override + public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) + { + return currentVisibility; + } + + @Override + public Iterable getItemSpawnSlots(GuiContainer gui, ItemStack item) { + return Collections.emptyList(); + } + + @Override + public List getInventoryAreas(GuiContainer gui) + { + return null; + } + + @Override + public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) + { + return false; + } + + @Override + public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) + { + return false; + } +} diff --git a/src/codechicken/nei/api/INEIGuiHandler.java b/src/codechicken/nei/api/INEIGuiHandler.java new file mode 100644 index 00000000..55b1111a --- /dev/null +++ b/src/codechicken/nei/api/INEIGuiHandler.java @@ -0,0 +1,48 @@ +package codechicken.nei.api; + +import codechicken.nei.VisiblityData; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; + +import java.util.List; + +/** + * If this is implemented on a gui, it will be automatically registered + */ +public interface INEIGuiHandler +{ + public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility); + + /** + * NEI will give the specified item to the InventoryRange returned if the player's inventory is full. + * return null for no range + */ + public Iterable getItemSpawnSlots(GuiContainer gui, ItemStack item); + + /** + * @return A list of TaggedInventoryAreas that will be used with the savestates. + */ + public List getInventoryAreas(GuiContainer gui); + + /** + * Handles clicks while an itemstack has been dragged from the item panel. Use this to set configurable slots and the like. + * Changes made to the stackSize of the dragged stack will be kept + * @param gui The current gui instance + * @param mousex The x position of the mouse + * @param mousey The y position of the mouse + * @param draggedStack The stack being dragged from the item panel + * @param button The button presed + * @return True if the drag n drop was handled. False to resume processing through other routes. The held stack will be deleted if draggedStack.stackSize == 0 + */ + public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button); + + /** + * Used to prevent the item panel from drawing on top of other gui elements. + * @param x The x coordinate of the rectangle bounding the slot + * @param y The y coordinate of the rectangle bounding the slot + * @param w The w coordinate of the rectangle bounding the slot + * @param h The h coordinate of the rectangle bounding the slot + * @return true if the item panel slot within the specified rectangle should not be rendered. + */ + public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h); +} diff --git a/src/codechicken/nei/api/INEIModeHandler.java b/src/codechicken/nei/api/INEIModeHandler.java new file mode 100644 index 00000000..9019764d --- /dev/null +++ b/src/codechicken/nei/api/INEIModeHandler.java @@ -0,0 +1,6 @@ +package codechicken.nei.api; + +public interface INEIModeHandler +{ + public boolean isModeValid(int mode); +} diff --git a/src/codechicken/nei/api/IOverlayHandler.java b/src/codechicken/nei/api/IOverlayHandler.java new file mode 100644 index 00000000..80271f4a --- /dev/null +++ b/src/codechicken/nei/api/IOverlayHandler.java @@ -0,0 +1,9 @@ +package codechicken.nei.api; + +import codechicken.nei.recipe.IRecipeHandler; +import net.minecraft.client.gui.inventory.GuiContainer; + +public interface IOverlayHandler +{ + public void overlayRecipe(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, boolean shift); +} diff --git a/src/codechicken/nei/api/IRecipeOverlayRenderer.java b/src/codechicken/nei/api/IRecipeOverlayRenderer.java new file mode 100644 index 00000000..8e659263 --- /dev/null +++ b/src/codechicken/nei/api/IRecipeOverlayRenderer.java @@ -0,0 +1,9 @@ +package codechicken.nei.api; + +import codechicken.nei.guihook.GuiContainerManager; +import net.minecraft.inventory.Slot; + +public interface IRecipeOverlayRenderer +{ + public void renderOverlay(GuiContainerManager gui, Slot slot); +} diff --git a/src/codechicken/nei/api/IStackPositioner.java b/src/codechicken/nei/api/IStackPositioner.java new file mode 100644 index 00000000..8d95fcb9 --- /dev/null +++ b/src/codechicken/nei/api/IStackPositioner.java @@ -0,0 +1,14 @@ +package codechicken.nei.api; + +import codechicken.nei.PositionedStack; + +import java.util.ArrayList; + +/** + * For repositioning recipes in overlay renderers. + * + */ +public interface IStackPositioner +{ + public ArrayList positionStacks(ArrayList ai); +} diff --git a/src/codechicken/nei/api/ItemInfo.java b/src/codechicken/nei/api/ItemInfo.java new file mode 100644 index 00000000..e0585b22 --- /dev/null +++ b/src/codechicken/nei/api/ItemInfo.java @@ -0,0 +1,431 @@ +package codechicken.nei.api; + +import codechicken.core.featurehack.GameDataManipulator; +import codechicken.nei.*; +import codechicken.nei.config.ArrayDumper; +import codechicken.nei.config.ItemPanelDumper; +import codechicken.nei.config.RegistryDumper; +import codechicken.nei.guihook.GuiContainerManager; +import codechicken.nei.recipe.RecipeItemInputHandler; +import com.google.common.collect.ArrayListMultimap; +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityList.EntityEggInfo; +import net.minecraft.entity.monster.EntityIronGolem; +import net.minecraft.entity.monster.EntitySnowman; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.inventory.Slot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.potion.Potion; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.RegistryNamespaced; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.common.BiomeDictionary; +import net.minecraftforge.common.IShearable; +import net.minecraftforge.oredict.OreDictionary; + +import java.util.*; + +/** + * This is an internal class for storing information about items, to be accessed by the API + */ +public class ItemInfo +{ + public static enum Layout + { + HEADER, BODY, FOOTER + } + + public static final HashMap> highlightHandlers = new HashMap>(); + public static final ItemStackMap nameOverrides = new ItemStackMap(); + public static final ItemStackSet hiddenItems = new ItemStackSet(); + public static final ItemStackSet finiteItems = new ItemStackSet(); + public static final ArrayListMultimap itemOverrides = ArrayListMultimap.create(); + + public static final LinkedList infiniteHandlers = new LinkedList(); + public static final ArrayListMultimap highlightIdentifiers = ArrayListMultimap.create(); + public static final HashSet> fastTransferExemptions = new HashSet>(); + + public static final HashMap itemOwners = new HashMap(); + + static { + for (Layout layout : Layout.values()) + ItemInfo.highlightHandlers.put(layout, new ArrayList()); + } + + public static boolean isHidden(ItemStack stack) { + return hiddenItems.contains(stack); + } + + public static boolean isHidden(Item item) { + return hiddenItems.containsAll(item); + } + + public static String getNameOverride(ItemStack stack) { + return nameOverrides.get(stack); + } + + public static boolean canBeInfinite(ItemStack stack) { + return !finiteItems.contains(stack); + } + + public static List getItemOverrides(Item item) { + return itemOverrides.get(item); + } + + public static void load(World world) { + addVanillaBlockProperties(); + addDefaultDropDowns(); + searchItems(); + parseModItems(); + addMobSpawnerItem(world); + addSpawnEggs(); + addInfiniteHandlers(); + addInputHandlers(); + addIDDumps(); + } + + private static void addIDDumps() { + API.addOption(new RegistryDumper("tools.dump.item") + { + @Override + public String[] header() { + return new String[]{"Name", "ID", "Has Block", "Mod", "Class"}; + } + + @Override + public String[] dump(Item item, int id, String name) { + return new String[]{ + name, + Integer.toString(id), + Boolean.toString(Block.getBlockFromItem(item) != Blocks.air), + ItemInfo.itemOwners.get(item), + item.getClass().getCanonicalName() + }; + } + + @Override + public RegistryNamespaced registry() { + return Item.itemRegistry; + } + }); + API.addOption(new RegistryDumper("tools.dump.block") + { + @Override + public String[] header() { + return new String[]{"Name", "ID", "Has Item", "Mod", "Class"}; + } + + @Override + public String[] dump(Block item, int id, String name) { + return new String[]{ + name, + Integer.toString(id), + Boolean.toString(Item.getItemFromBlock(item) != null), + ItemInfo.itemOwners.get(item), + item.getClass().getCanonicalName() + }; + } + + @Override + public RegistryNamespaced registry() { + return Block.blockRegistry; + } + }); + API.addOption(new ArrayDumper("tools.dump.potion") + { + public String[] header() { + return new String[]{"ID", "Unlocalised name", "Class"}; + } + + @Override + public String[] dump(Potion potion, int id) { + return new String[]{ + Integer.toString(id), + potion.getName(), + potion.getClass().getCanonicalName() + }; + } + + @Override + public Potion[] array() { + return Potion.potionTypes; + } + }); + API.addOption(new ArrayDumper("tools.dump.enchantment") + { + public String[] header() { + return new String[]{"ID", "Unlocalised name", "Type", "Min Level", "Max Level", "Class"}; + } + + @Override + public String[] dump(Enchantment ench, int id) { + return new String[]{ + Integer.toString(id), + ench.getName(), + ench.type.toString(), + Integer.toString(ench.getMinLevel()), + Integer.toString(ench.getMaxLevel()), + ench.getClass().getCanonicalName() + }; + } + + @Override + public Enchantment[] array() { + return Enchantment.enchantmentsList; + } + }); + API.addOption(new ArrayDumper("tools.dump.biome") + { + @Override + public String[] header() { + return new String[]{"ID", "Name", "Temperature", "Rainfall", "Spawn Chance", "Root Height", "Height Variation", "Types", "Class"}; + } + + @Override + public String[] dump(BiomeGenBase biome, int id) { + BiomeDictionary.Type[] types = BiomeDictionary.getTypesForBiome(biome); + StringBuilder s_types = new StringBuilder(); + for (BiomeDictionary.Type t : types) { + if (s_types.length() > 0) + s_types.append(", "); + s_types.append(t.name()); + } + + return new String[]{ + Integer.toString(id), + biome.biomeName, + Float.toString(biome.getFloatTemperature(0, 0, 0)), + Float.toString(biome.getFloatRainfall()), + Float.toString(biome.getSpawningChance()), + Float.toString(biome.rootHeight), + Float.toString(biome.heightVariation), + s_types.toString(), + biome.getClass().getCanonicalName() + }; + } + + @Override + public BiomeGenBase[] array() { + return BiomeGenBase.getBiomeGenArray(); + } + }); + API.addOption(new ItemPanelDumper("tools.dump.itempanel")); + } + + private static void parseModItems() { + //TODO GameRegistry.findUniqueIdentifierFor() + } + + private static void addInputHandlers() { + GuiContainerManager.addInputHandler(new RecipeItemInputHandler()); + GuiContainerManager.addInputHandler(new PopupInputHandler()); + } + + private static void addMobSpawnerItem(World world) { + GameDataManipulator.replaceItem(Block.getIdFromBlock(Blocks.mob_spawner), new ItemMobSpawner(world)); + } + + private static void addInfiniteHandlers() { + API.addInfiniteItemHandler(new InfiniteStackSizeHandler()); + API.addInfiniteItemHandler(new InfiniteToolHandler()); + } + + private static void addVanillaBlockProperties() { + API.setOverrideName(new ItemStack(Blocks.flowing_water), "Water Source"); + API.setOverrideName(new ItemStack(Blocks.water), "Water Still"); + API.setOverrideName(new ItemStack(Blocks.flowing_lava), "Lava Source"); + API.setOverrideName(new ItemStack(Blocks.lava), "Lava Still"); + API.setOverrideName(new ItemStack(Blocks.end_portal), "End Portal"); + API.setOverrideName(new ItemStack(Blocks.end_portal_frame), "End Portal Frame"); + API.hideItem(new ItemStack(Blocks.double_stone_slab, 1, OreDictionary.WILDCARD_VALUE)); + API.hideItem(new ItemStack(Blocks.double_wooden_slab, 1, OreDictionary.WILDCARD_VALUE)); + API.hideItem(new ItemStack(Blocks.carrots)); + API.hideItem(new ItemStack(Blocks.potatoes)); + API.hideItem(new ItemStack(Blocks.cocoa)); + } + + private static void addDefaultDropDowns() { + /*API.addSetRange("Blocks", new MultiItemRange("[0-32000]") + { + @Override + public void addItemIfInRange(int item, int damage, NBTTagCompound compound) { + if (item < Block.blocksList.length && (Block.blocksList[item] != null && Block.blocksList[item].blockMaterial != Material.air)) + super.addItemIfInRange(item, damage, compound); + } + }); + API.addSetRange("Items", new MultiItemRange("[0-32000]") + { + @Override + public void addItemIfInRange(int item, int damage, NBTTagCompound compound) { + if (item >= Block.blocksList.length || Block.blocksList[item] == null || Block.blocksList[item].blockMaterial == Material.air) + super.addItemIfInRange(item, damage, compound); + } + }); + API.addSetRange("Blocks.MobSpawners", new MultiItemRange("[52]"));*/ + } + + //TODO + private static void searchItems() { + /*MultiItemRange tools = new MultiItemRange(); + MultiItemRange picks = new MultiItemRange(); + MultiItemRange shovels = new MultiItemRange(); + MultiItemRange axes = new MultiItemRange(); + MultiItemRange hoes = new MultiItemRange(); + MultiItemRange swords = new MultiItemRange(); + MultiItemRange chest = new MultiItemRange(); + MultiItemRange helmets = new MultiItemRange(); + MultiItemRange legs = new MultiItemRange(); + MultiItemRange boots = new MultiItemRange(); + MultiItemRange other = new MultiItemRange(); + MultiItemRange ranged = new MultiItemRange(); + MultiItemRange food = new MultiItemRange(); + MultiItemRange potioningredients = new MultiItemRange(); + + MultiItemRange[] creativeTabRanges = new MultiItemRange[CreativeTabs.creativeTabArray.length]; + for (CreativeTabs tab : CreativeTabs.creativeTabArray) + creativeTabRanges[tab.getTabIndex()] = new MultiItemRange(); + + for (Item item : (Iterable) Item.itemRegistry) { + if (item == null) + continue; + + CreativeTabs itemTab = item.getCreativeTab(); + if (itemTab != null) + creativeTabRanges[itemTab.getTabIndex()].add(item); + + if (item.isDamageable()) { + tools.add(item); + if (item instanceof ItemPickaxe) { + picks.add(item); + } else if (item instanceof ItemSpade) { + shovels.add(item); + } else if (item instanceof ItemAxe) { + axes.add(item); + } else if (item instanceof ItemHoe) { + hoes.add(item); + } else if (item instanceof ItemSword) { + swords.add(item); + } else if (item instanceof ItemArmor) { + switch (((ItemArmor) item).armorType) { + case 0: + helmets.add(item); + break; + case 1: + chest.add(item); + break; + case 2: + legs.add(item); + break; + case 3: + boots.add(item); + break; + } + } else if (item == Items.arrow || item == Items.bow) { + ranged.add(item); + } else if (item == Items.fishing_rod || item == Items.flint_and_steel || item == Items.shears) { + other.add(item); + } + } + + if (item instanceof ItemFood) { + food.add(item); + } + + if (item.isPotionIngredient()) { + BrewingRecipeHandler.ingredients.add(item); + potioningredients.add(item); + } + } + API.addSetRange("Items.Tools.Pickaxes", picks); + API.addSetRange("Items.Tools.Shovels", shovels); + API.addSetRange("Items.Tools.Axes", axes); + API.addSetRange("Items.Tools.Hoes", hoes); + API.addSetRange("Items.Tools.Other", other); + API.addSetRange("Items.Weapons.Swords", swords); + API.addSetRange("Items.Weapons.Ranged", ranged); + API.addSetRange("Items.Armor.ChestPlates", chest); + API.addSetRange("Items.Armor.Leggings", legs); + API.addSetRange("Items.Armor.Helmets", helmets); + API.addSetRange("Items.Armor.Boots", boots); + API.addSetRange("Items.Food", food); + API.addSetRange("Items.Potions.Ingredients", potioningredients); + + for (CreativeTabs tab : CreativeTabs.creativeTabArray) { + if (creativeTabRanges[tab.getTabIndex()].ranges.size() > 0) + API.addSetRange("CreativeTabs." + LangUtil.translateG(tab.getTranslatedTabLabel()), creativeTabRanges[tab.getTabIndex()]); + }*/ + } + + private static void addSpawnEggs() + { + addEntityEgg(EntitySnowman.class, 0xEEFFFF, 0xffa221); + addEntityEgg(EntityIronGolem.class, 0xC5C2C1, 0xffe1cc); + } + + private static void addEntityEgg(Class entity, int i, int j) { + int id = (Integer)EntityList.classToIDMapping.get(entity); + EntityList.entityEggs.put(id, new EntityEggInfo(id, i, j)); + } + + public static ArrayList getIdentifierItems(World world, EntityPlayer player, MovingObjectPosition hit) { + int x = hit.blockX; + int y = hit.blockY; + int z = hit.blockZ; + Block mouseoverBlock = world.getBlock(x, y, z); + + ArrayList items = new ArrayList(); + + ArrayList handlers = new ArrayList(); + if (highlightIdentifiers.containsKey(null)) + handlers.addAll(highlightIdentifiers.get(null)); + if (highlightIdentifiers.containsKey(mouseoverBlock)) + handlers.addAll(highlightIdentifiers.get(mouseoverBlock)); + for (IHighlightHandler ident : handlers) { + ItemStack item = ident.identifyHighlight(world, player, hit); + if (item != null) + items.add(item); + } + + if (items.size() > 0) + return items; + + ItemStack pick = mouseoverBlock.getPickBlock(hit, world, x, y, z); + if (pick != null) + items.add(pick); + + try { + items.addAll(mouseoverBlock.getDrops(world, x, y, z, world.getBlockMetadata(x, y, z), 0)); + } catch (Exception ignored) {} + if (mouseoverBlock instanceof IShearable) { + IShearable shearable = (IShearable) mouseoverBlock; + if (shearable.isShearable(new ItemStack(Items.shears), world, x, y, z)) + items.addAll(shearable.onSheared(new ItemStack(Items.shears), world, x, y, z, 0)); + } + + if (items.size() == 0) + items.add(0, new ItemStack(mouseoverBlock, 1, world.getBlockMetadata(x, y, z))); + + return items; + } + + public static void registerHighlightHandler(IHighlightHandler handler, ItemInfo.Layout... layouts) { + for (ItemInfo.Layout layout : layouts) + ItemInfo.highlightHandlers.get(layout).add(handler); + } + + public static List getText(ItemStack itemStack, World world, EntityPlayer player, MovingObjectPosition mop) { + List retString = new ArrayList(); + + for (ItemInfo.Layout layout : ItemInfo.Layout.values()) + for (IHighlightHandler handler : ItemInfo.highlightHandlers.get(layout)) + retString = handler.handleTextData(itemStack, world, player, mop, retString, layout); + + return retString; + } +} diff --git a/src/codechicken/nei/api/LayoutStyle.java b/src/codechicken/nei/api/LayoutStyle.java new file mode 100644 index 00000000..11346b6f --- /dev/null +++ b/src/codechicken/nei/api/LayoutStyle.java @@ -0,0 +1,20 @@ +package codechicken.nei.api; + +import codechicken.nei.Button; +import codechicken.nei.VisiblityData; +import codechicken.nei.guihook.GuiContainerManager; +import net.minecraft.client.gui.inventory.GuiContainer; + +public abstract class LayoutStyle +{ + public abstract void init(); + public abstract void reset(); + public abstract void layout(GuiContainer gui, VisiblityData visibility); + public abstract String getName(); + + public void drawBackground(GuiContainerManager gui) + { + } + public abstract void drawButton(Button button, int mousex, int mousey); + public abstract boolean texturedButtons(); +} diff --git a/src/codechicken/nei/api/NEIInfo.java b/src/codechicken/nei/api/NEIInfo.java new file mode 100644 index 00000000..561145eb --- /dev/null +++ b/src/codechicken/nei/api/NEIInfo.java @@ -0,0 +1,29 @@ +package codechicken.nei.api; + +import codechicken.nei.NEIClientConfig; +import codechicken.nei.config.OptionCycled; +import net.minecraft.world.World; + +import java.util.LinkedList; + +public class NEIInfo +{ + public static final LinkedList modeHandlers = new LinkedList(); + + public static void load(World world) { + OptionCycled modeOption = (OptionCycled) NEIClientConfig.getOptionList().getOption("inventory.cheatmode"); + modeOption.parent.synthesizeEnvironment(); + if(!modeOption.optionValid(modeOption.value())) { + modeOption.copyGlobals(); + modeOption.cycle(); + } + } + + public static boolean isValidMode(int mode) { + for(INEIModeHandler handler : modeHandlers) + if(!handler.isModeValid(mode)) + return false; + + return true; + } +} diff --git a/src/codechicken/nei/api/TaggedInventoryArea.java b/src/codechicken/nei/api/TaggedInventoryArea.java new file mode 100644 index 00000000..b50d2e23 --- /dev/null +++ b/src/codechicken/nei/api/TaggedInventoryArea.java @@ -0,0 +1,48 @@ +package codechicken.nei.api; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; + +import java.util.HashSet; + +public class TaggedInventoryArea +{ + public HashSet slots = new HashSet(); + public String tagName; + /** + * Only for player inventories. + */ + private IInventory inventory; + /** + * Other inventories + */ + private Container container; + + public TaggedInventoryArea(InventoryPlayer invPlayer) + { + this("InventoryPlayer", 0, 39, null); + inventory = invPlayer; + } + + public TaggedInventoryArea(String name, int first, int last, Container container) + { + this.container = container; + tagName = name; + for(int i = first; i <= last; i++) + slots.add(i); + } + + public ItemStack getStackInSlot(int i) + { + if(inventory != null) + return inventory.getStackInSlot(i); + return container.getSlot(i).getStack(); + } + + public boolean isContainer() + { + return inventory == null; + } +} diff --git a/src/codechicken/nei/asm/NEICorePlugin.java b/src/codechicken/nei/asm/NEICorePlugin.java new file mode 100644 index 00000000..f3906c5b --- /dev/null +++ b/src/codechicken/nei/asm/NEICorePlugin.java @@ -0,0 +1,42 @@ +package codechicken.nei.asm; + +import codechicken.core.launch.CodeChickenCorePlugin; +import cpw.mods.fml.relauncher.IFMLCallHook; +import cpw.mods.fml.relauncher.IFMLLoadingPlugin; +import cpw.mods.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions; + +import java.util.Map; + +@TransformerExclusions(value = {"codechicken.nei.asm"}) +public class NEICorePlugin implements IFMLLoadingPlugin, IFMLCallHook +{ + @Override + public String[] getASMTransformerClass() { + CodeChickenCorePlugin.versionCheck(CodeChickenCorePlugin.mcVersion, "NotEnoughItems"); + return new String[]{"codechicken.nei.asm.NEITransformer"}; + } + + @Override + public String getModContainerClass() { + return "codechicken.nei.NEIModContainer"; + } + + @Override + public String getSetupClass() { + return "codechicken.nei.asm.NEICorePlugin"; + } + + @Override + public void injectData(Map data) { + } + + @Override + public String getAccessTransformerClass() { + return null; + } + + @Override + public Void call() { + return null; + } +} diff --git a/src/codechicken/nei/asm/NEITransformer.java b/src/codechicken/nei/asm/NEITransformer.java new file mode 100644 index 00000000..bfbb5a75 --- /dev/null +++ b/src/codechicken/nei/asm/NEITransformer.java @@ -0,0 +1,180 @@ +package codechicken.nei.asm; + +import codechicken.lib.asm.*; +import codechicken.lib.asm.ModularASMTransformer.*; +import cpw.mods.fml.relauncher.FMLLaunchHandler; +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.util.TraceClassVisitor; + +import java.io.*; +import java.util.Map; + +import static codechicken.lib.asm.InsnComparator.*; +import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; +import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS; +import static org.objectweb.asm.Opcodes.*; + +public class NEITransformer implements IClassTransformer +{ + private ModularASMTransformer transformer = new ModularASMTransformer(); + private Map asmblocks = ASMReader.loadResource("/assets/nei/asm/blocks.asm"); + + public NEITransformer() { + //Generates method to set the placed position of a mob spawner for the item callback. More portable than copying vanilla placement code + transformer.add(new MethodWriter(ACC_PUBLIC, + new ObfMapping("net/minecraft/block/BlockMobSpawner", "func_149689_a", "(Lnet/minecraft/world/World;IIILnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;)V"), + asmblocks.get("spawnerPlaced"))); + + //Make MobSpawnerBaseLogic use getSpawnerWorld when creating new entities + transformer.add(new MethodReplacer(new ObfMapping("net/minecraft/tileentity/MobSpawnerBaseLogic", "func_98281_h", "()Lnet/minecraft/entity/Entity;"), + asmblocks.get("d_spawnerWorld"), asmblocks.get("spawnerWorld"))); + + //Removes trailing seperators from NBTTagList/Compound.toString because OCD + transformer.add(new MethodInjector(new ObfMapping("net/minecraft/nbt/NBTTagCompound", "toString", "()Ljava/lang/String;"), + asmblocks.get("n_commaFix"), asmblocks.get("commaFix"), true)); + transformer.add(new MethodInjector(new ObfMapping("net/minecraft/nbt/NBTTagList", "toString", "()Ljava/lang/String;"), + asmblocks.get("n_commaFix"), asmblocks.get("commaFix"), true)); + + String GuiContainer = "net/minecraft/client/gui/inventory/GuiContainer"; + //add manager field + transformer.add(new FieldWriter(ACC_PUBLIC, new ObfMapping(GuiContainer, "manager", "Lcodechicken/nei/guihook/GuiContainerManager;"))); + + //Fill out getManager in GuiContainerManager + transformer.add(new MethodWriter(ACC_PUBLIC | ACC_STATIC, + new ObfMapping("codechicken/nei/guihook/GuiContainerManager", "getManager", "(Lnet/minecraft/client/gui/inventory/GuiContainer;)Lcodechicken/nei/guihook/GuiContainerManager;"), + asmblocks.get("m_getManager"))); + + + //Generate load method + transformer.add(new MethodWriter(ACC_PUBLIC, new ObfMapping(GuiContainer, "func_146280_a", "(Lnet/minecraft/client/Minecraft;II)V"), asmblocks.get("m_setWorldAndResolution"))); + + //Generate handleKeyboardInput method + transformer.add(new MethodWriter(ACC_PUBLIC, new ObfMapping(GuiContainer, "func_146282_l", "()V"), asmblocks.get("m_handleKeyboardInput"))); + + //Generate handleKeyboardInput method + transformer.add(new MethodWriter(ACC_PUBLIC, new ObfMapping(GuiContainer, "func_146282_l", "()V"), asmblocks.get("m_handleKeyboardInput"))); + + //Generate handleKeyboardInput method + transformer.add(new MethodWriter(ACC_PUBLIC, new ObfMapping(GuiContainer, "func_146282_l", "()V"), asmblocks.get("m_handleKeyboardInput"))); + + //Generate handleMouseInput method + transformer.add(new MethodWriter(ACC_PUBLIC, new ObfMapping(GuiContainer, "func_146274_d", "()V"), asmblocks.get("m_handleMouseInput"))); + + //Generate public accessor for keyTyped + transformer.add(new MethodWriter(ACC_PUBLIC, new ObfMapping(GuiContainer, "publicKeyTyped", "(CI)V"), asmblocks.get("m_publicKeyTyped"))); + + //Fill out callKeyTyped in GuiContainerManager + transformer.add(new MethodWriter(ACC_PUBLIC | ACC_STATIC, + new ObfMapping("codechicken/nei/guihook/GuiContainerManager", "callKeyTyped", "(Lnet/minecraft/client/gui/inventory/GuiContainer;CI)V"), + asmblocks.get("m_callKeyTyped"))); + //Inject preDraw at the start of drawScreen + transformer.add(new MethodInjector(new ObfMapping(GuiContainer, "func_73863_a", "(IIF)V"), asmblocks.get("preDraw"), true)); + + //Inject objectUnderMouse check before drawing slot highlights + transformer.add(new MethodInjector(new ObfMapping(GuiContainer, "func_73863_a", "(IIF)V"), asmblocks.get("n_objectUnderMouse"), asmblocks.get("objectUnderMouse"), false)); + + //Inject renderObjects after drawGuiContainerForegroundLayer + transformer.add(new MethodInjector(new ObfMapping(GuiContainer, "func_73863_a", "(IIF)V"), asmblocks.get("n_renderObjects"), asmblocks.get("renderObjects"), false)); + + //Replace default renderToolTip with delegate + transformer.add(new MethodReplacer(new ObfMapping(GuiContainer, "func_73863_a", "(IIF)V"), asmblocks.get("d_renderToolTip"), asmblocks.get("renderTooltips"))); + + //Replace zLevel = 200 with zLevel = 500 in drawItemStack + transformer.add(new MethodReplacer(new ObfMapping(GuiContainer, "func_146982_a", "(Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V"), + asmblocks.get("d_zLevel"), asmblocks.get("zLevel"))); + + //Replace default renderItem with delegate and slot overlay/underlay + transformer.add(new MethodReplacer(new ObfMapping(GuiContainer, "func_146977_a", "(Lnet/minecraft/inventory/Slot;)V"), asmblocks.get("d_drawSlot"), asmblocks.get("drawSlot"))); + + //Inject mouseClicked hook after super call in mouseClicked + transformer.add(new MethodInjector(new ObfMapping(GuiContainer, "func_73864_a", "(III)V"), asmblocks.get("n_mouseClicked"), asmblocks.get("mouseClicked"), false)); + + //Replace general handleMouseClicked call with delegate + transformer.add(new MethodReplacer(new ObfMapping(GuiContainer, "func_73864_a", "(III)V"), asmblocks.get("d_handleMouseClick"), asmblocks.get("handleMouseClick"))); + + //Inject mouseDragged hook after super call in mouseDragged + transformer.add(new MethodInjector(new ObfMapping(GuiContainer, "func_146273_a", "(IIIJ)V"), asmblocks.get("n_mouseDragged"), asmblocks.get("mouseDragged"), false)); + + //Inject overrideMouseUp at the start of mouseMovedOrUp + transformer.add(new MethodInjector(new ObfMapping(GuiContainer, "func_146286_b", "(III)V"), asmblocks.get("overrideMouseUp"), true)); + + //Replace general handleMouseClicked call with delegate + transformer.add(new MethodReplacer(new ObfMapping(GuiContainer, "func_146286_b", "(III)V"), asmblocks.get("d_handleMouseClick2"), asmblocks.get("handleMouseClick2"))); + + transformer.add(new MethodTransformer(new ObfMapping(GuiContainer, "func_146286_b", "(III)V")) + { + @Override + public void transform(MethodNode mv) { + ASMBlock gotoBlock = asmblocks.get("n_mouseUpGoto"); + ASMBlock needleBlock = asmblocks.get("n_mouseUp"); + ASMBlock injectionBlock = asmblocks.get("mouseUp"); + + gotoBlock.mergeLabels(injectionBlock); + findOnce(mv.instructions, gotoBlock.list).replace(gotoBlock.list.list); + + InsnListSection needle = findOnce(mv.instructions, needleBlock.list); + injectionBlock.mergeLabels(needleBlock.applyLabels(needle)); + needle.insertBefore(injectionBlock.list.list); + } + }); + + //Replace general handleSlotClick call with delegate + transformer.add(new MethodReplacer(new ObfMapping(GuiContainer, "func_146984_a", "(Lnet/minecraft/inventory/Slot;III)V"), + asmblocks.get("d_handleSlotClick"), asmblocks.get("handleSlotClick"))); + + //Inject lastKeyTyped at the start of keyTyped + transformer.add(new MethodInjector(new ObfMapping(GuiContainer, "func_73869_a", "(CI)V"), asmblocks.get("lastKeyTyped"), true)); + + //Inject updateScreen hook after super call + transformer.add(new MethodInjector(new ObfMapping(GuiContainer, "func_73876_c", "()V"), asmblocks.get("n_updateScreen"), asmblocks.get("updateScreen"), false)); + } + + private ObfMapping c_GuiContainer = new ObfMapping("net/minecraft/client/gui/inventory/GuiContainer").toClassloading(); + /** + * Adds super.updateScreen() to non implementing GuiContainer subclasses + */ + public byte[] transformSubclasses(String name, byte[] bytes) { + if (ClassHeirachyManager.classExtends(name, c_GuiContainer.javaClass())) { + ClassNode cnode = ASMHelper.createClassNode(bytes); + + ObfMapping methodmap = new ObfMapping(cnode.superName, "func_73876_c", "()V").toClassloading(); + + InsnListSection supercall = new InsnListSection(); + supercall.add(new VarInsnNode(ALOAD, 0)); + supercall.add(methodmap.toInsn(INVOKESPECIAL)); + + boolean changed = false; + for (MethodNode mv : cnode.methods) { + if (methodmap.matches(mv)) { + if (matches(new InsnListSection(mv.instructions), supercall, getControlFlowLabels(mv.instructions)) == null) { + mv.instructions.insert(supercall.list); + ASMHelper.logger.debug("Inserted super call into " + methodmap); + changed = true; + } + } + } + + if (changed) + bytes = ASMHelper.createBytes(cnode, COMPUTE_MAXS | COMPUTE_FRAMES); + } + return bytes; + } + + @Override + public byte[] transform(String name, String tname, byte[] bytes) { + if (bytes == null) return null; + try { + if (FMLLaunchHandler.side().isClient()) + bytes = transformSubclasses(name, bytes); + + bytes = transformer.transform(name, bytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return bytes; + } +} diff --git a/src/codechicken/nei/config/ArrayDumper.java b/src/codechicken/nei/config/ArrayDumper.java new file mode 100644 index 00000000..46c25437 --- /dev/null +++ b/src/codechicken/nei/config/ArrayDumper.java @@ -0,0 +1,31 @@ +package codechicken.nei.config; + +import java.util.LinkedList; + +public abstract class ArrayDumper extends DataDumper +{ + public ArrayDumper(String name) { + super(name); + } + + @Override + public Iterable dump(int mode) { + LinkedList list = new LinkedList(); + T[] array = array(); + for (int i = 0; i < array.length; i++) { + T obj = array[i]; + if (obj == null) { + if (mode == 1 || mode == 2) + list.add(new String[]{Integer.toString(i), null, null, null, null}); + } else { + if (mode == 0 || mode == 2) + list.add(dump(obj, i)); + } + } + return list; + } + + public abstract T[] array(); + + public abstract String[] dump(T obj, int id); +} \ No newline at end of file diff --git a/src/codechicken/nei/config/ConfigSet.java b/src/codechicken/nei/config/ConfigSet.java new file mode 100644 index 00000000..157c5bba --- /dev/null +++ b/src/codechicken/nei/config/ConfigSet.java @@ -0,0 +1,42 @@ +package codechicken.nei.config; + +import codechicken.lib.config.ConfigTagParent; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; + +import java.io.File; + +public class ConfigSet +{ + private File nbtFile; + public NBTTagCompound nbt; + public ConfigTagParent config; + + public ConfigSet(File nbtFile, ConfigTagParent config) { + this.nbtFile = nbtFile; + this.config = config; + loadNBT(); + } + + public void loadNBT() { + nbt = new NBTTagCompound(); + try { + if (!nbtFile.getParentFile().exists()) + nbtFile.getParentFile().mkdirs(); + if (!nbtFile.exists()) + nbtFile.createNewFile(); + if (nbtFile.length() > 0) + nbt = CompressedStreamTools.read(nbtFile); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void saveNBT() { + try { + CompressedStreamTools.write(nbt, nbtFile); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/codechicken/nei/config/DataDumper.java b/src/codechicken/nei/config/DataDumper.java new file mode 100644 index 00000000..9b366121 --- /dev/null +++ b/src/codechicken/nei/config/DataDumper.java @@ -0,0 +1,160 @@ +package codechicken.nei.config; + +import codechicken.core.CommonUtils; +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.LayoutManager; +import codechicken.nei.NEIClientUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.opengl.GL11; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; + +import static codechicken.lib.gui.GuiDraw.drawString; +import static codechicken.lib.gui.GuiDraw.drawStringC; + +public abstract class DataDumper extends Option +{ + public DataDumper(String name) { + super(name); + } + + public abstract String[] header(); + + public abstract Iterable dump(int mode); + + public String renderName() { + return translateN(name + "s"); + } + + public void dumpFile() { + try { + File file = new File(CommonUtils.getMinecraftDir(), "dumps/" + getFileName(name.replaceFirst(".+\\.", ""))); + if (!file.getParentFile().exists()) + file.getParentFile().mkdirs(); + if (!file.exists()) + file.createNewFile(); + + dumpTo(file); + + NEIClientUtils.printChatMessage(dumpMessage(file)); + } catch (Exception e) { + System.err.println("Error dumping " + renderName() + " mode: " + getMode()); + e.printStackTrace(); + } + } + + public String getFileName(String prefix) { + return prefix + getFileExtension(); + } + + public String getFileExtension() { + return ".csv"; + } + + public IChatComponent dumpMessage(File file) { + return new ChatComponentTranslation("nei.options.tools.dump.dumped", + translateN(name), "dumps/" + file.getName()); + } + + public void dumpTo(File file) throws IOException { + int mode = getMode(); + PrintWriter w = new PrintWriter(file); + w.println(concat(header())); + for (String[] line : dump(mode)) + w.println(concat(line)); + w.close(); + } + + public static String concat(String[] header) { + StringBuilder sb = new StringBuilder(); + for (String s : header) { + if (sb.length() > 0) + sb.append(','); + if (s == null) + s = "null"; + if (s.indexOf(',') > 0 || s.indexOf('\"') > 0) + s = '\"' + s.replace("\"", "\"\"") + '\"'; + sb.append(s); + } + return sb.toString(); + } + + @Override + public void draw(int mousex, int mousey, float frame) { + drawPrefix(); + if(modeCount() > 1) + drawModeButton(mousex, mousey); + drawDumpButton(mousex, mousey); + } + + public void drawPrefix() { + drawString(renderName(), 10, 8, -1); + } + + public Rectangle4i dumpButtonSize() { + int width = 80; + return new Rectangle4i(slot.contentWidth() - width, 2, width, 20); + } + + public Rectangle4i modeButtonSize() { + int width = 60; + return new Rectangle4i(slot.contentWidth() - width - 10 - dumpButtonSize().w, 2, width, 20); + } + + public String dumpButtonText() { + return NEIClientUtils.lang.translate("options.tools.dump.dump"); + } + + public String modeButtonText() { + return NEIClientUtils.lang.translate("options.tools.dump.mode." + getMode()); + } + + public int getMode() { + return getTag().getIntValue(0); + } + + public void drawModeButton(int mousex, int mousey) { + GL11.glColor4f(1, 1, 1, 1); + Rectangle4i b = modeButtonSize(); + boolean hover = b.contains(mousex, mousey); + LayoutManager.drawButtonBackground(b.x, b.y, b.w, b.h, true, getButtonTex(hover)); + drawStringC(modeButtonText(), b.x, b.y, b.w, b.h, getTextColour(hover)); + } + + public void drawDumpButton(int mousex, int mousey) { + GL11.glColor4f(1, 1, 1, 1); + Rectangle4i b = dumpButtonSize(); + boolean hover = b.contains(mousex, mousey); + LayoutManager.drawButtonBackground(b.x, b.y, b.w, b.h, true, getButtonTex(hover)); + drawStringC(dumpButtonText(), b.x, b.y, b.w, b.h, getTextColour(hover)); + } + + public int getButtonTex(boolean hover) { + return hover ? 2 : 1; + } + + public int getTextColour(boolean hover) { + return hover ? 0xFFFFFFA0 : 0xFFE0E0E0; + } + + @Override + public void mouseClicked(int mousex, int mousey, int button) { + if (modeCount() > 1 && modeButtonSize().contains(mousex, mousey)) { + Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); + getTag().setIntValue((getMode() + 1) % modeCount()); + } else if (dumpButtonSize().contains(mousex, mousey)) { + Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); + dumpFile(); + } + } + + public int modeCount() { + return 3; + } +} diff --git a/src/codechicken/nei/config/GuiHighlightTips.java b/src/codechicken/nei/config/GuiHighlightTips.java new file mode 100644 index 00000000..b2587f62 --- /dev/null +++ b/src/codechicken/nei/config/GuiHighlightTips.java @@ -0,0 +1,149 @@ +package codechicken.nei.config; + +import codechicken.core.gui.GuiCCButton; +import codechicken.core.gui.GuiScreenWidget; +import codechicken.lib.math.MathHelper; +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.HUDRenderer; +import codechicken.nei.NEIClientUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import org.lwjgl.input.Keyboard; + +import java.awt.*; +import java.util.Arrays; + +import static codechicken.lib.gui.GuiDraw.displaySize; +import static codechicken.lib.gui.GuiDraw.getMousePosition; + +public class GuiHighlightTips extends GuiScreenWidget +{ + private String name; + private GuiCCButton toggleButton; + private OptionHighlightTips opt; + private Point dragDown; + + public GuiHighlightTips(OptionHighlightTips opt) { + super(80, 20); + this.opt = opt; + name = opt.name; + } + + @Override + public boolean doesGuiPauseGame() { + return true; + } + + public void addWidgets() { + add(toggleButton = new GuiCCButton(0, 0, 80, 20, "").setActionCommand("show")); + updateNames(); + } + + @Override + public void actionPerformed(String ident, Object... params) { + if (ident.equals("show")) { + opt.getTag(name).setBooleanValue(!show()); + updateNames(); + } + } + + private void updateNames() { + toggleButton.text = NEIClientUtils.translate("options." + name + "." + (show() ? "show" : "hide")); + } + + private boolean show() { + return opt.getTag(name).getBooleanValue(); + } + + @Override + public void keyTyped(char c, int keycode) { + if (keycode == Keyboard.KEY_ESCAPE || keycode == Keyboard.KEY_BACK) { + Minecraft.getMinecraft().displayGuiScreen(opt.slot.getGui()); + return; + } + super.keyTyped(c, keycode); + } + + @Override + public void drawScreen(int mousex, int mousey, float f) { + super.drawScreen(mousex, mousey, f); + if (show()) + HUDRenderer.renderOverlay(new ItemStack(Blocks.redstone_block), Arrays.asList("RedstoneBlock", EnumChatFormatting.RED+"Sample"), renderPos()); + } + + public Point getPos() { + return new Point(opt.getTag(name + ".x").getIntValue(), opt.getTag(name + ".y").getIntValue()); + } + + public Dimension sampleSize()//copied from HUDManager when running with the sample for this gui + { + return new Dimension(101, 30); + } + + public Point getDrag() { + Point mouse = getMousePosition(); + Point drag = new Point(mouse.x - dragDown.x, mouse.y - dragDown.y); + Dimension size = displaySize(); + Dimension sample = sampleSize(); + drag.x *= 10000; + drag.y *= 10000; + drag.x /= (size.width - sample.width); + drag.y /= (size.height - sample.height); + Point pos = getPos(); + drag.x = (int) MathHelper.clip(drag.x, -pos.x, 10000 - pos.x); + drag.y = (int) MathHelper.clip(drag.y, -pos.y, 10000 - pos.y); + return drag; + } + + public Point renderPos() { + Point pos = getPos(); + if (dragDown != null) { + Point drag = getDrag(); + pos.x += drag.x; + pos.y += drag.y; + } + + for (int i = 25; i < 100; i += 25)//snapping + { + if (pos.x / 100 == i) + pos.x = i * 100; + if (pos.y / 100 == i) + pos.y = i * 100; + } + + return pos; + } + + @Override + protected void mouseMovedOrUp(int x, int y, int button) { + if (button == 0 && dragDown != null) { + setPos(renderPos()); + dragDown = null; + } + } + + public Rectangle4i selectionBox() { + Point pos = renderPos(); + Dimension size = displaySize(); + Dimension rect = sampleSize(); + return new Rectangle4i( + (size.width - rect.width) * pos.x / 10000, + (size.height - rect.height) * pos.y / 10000, + rect.width, rect.height); + } + + @Override + protected void mouseClicked(int x, int y, int button) { + if (button == 0 && selectionBox().contains(x, y)) + dragDown = getMousePosition(); + else + super.mouseClicked(x, y, button); + } + + private void setPos(Point p) { + opt.getTag(name + ".x").setIntValue(p.x); + opt.getTag(name + ".y").setIntValue(p.y); + } +} diff --git a/src/codechicken/nei/config/GuiOptionList.java b/src/codechicken/nei/config/GuiOptionList.java new file mode 100644 index 00000000..93295bc3 --- /dev/null +++ b/src/codechicken/nei/config/GuiOptionList.java @@ -0,0 +1,378 @@ +package codechicken.nei.config; + +import codechicken.core.gui.GuiCCButton; +import codechicken.core.gui.GuiScreenWidget; +import codechicken.core.gui.GuiScrollSlot; +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.LayoutManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.opengl.GL11; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import static codechicken.lib.gui.GuiDraw.*; +import static net.minecraft.util.StatCollector.translateToLocal; + +public class GuiOptionList extends GuiScreenWidget +{ + public class OptionScrollSlot extends GuiScrollSlot + { + public ArrayList