Compare commits

...
Author SHA1 Message Date
Eldrinn-Elantey 8fe64a5ee0 Move infusion logic from TileVat to TileVatMatrix
TileVat now handles only heal/clone/revive (modes 0, 1, 3, 4).
TileVatMatrix owns all infusion state and the craft cycle.
SourceFX moved to TileVatMatrix; GuiVat and renderers updated accordingly.
2026-05-15 09:10:32 +04:00
Eldrinn-Elantey 1ef8e16e9c Fix stabilizer restoring hungry node type when second stabilizer is still active (#121) 2026-05-14 14:36:08 -07:00
Eldrinn-Elanteyandthehoblit a084621884 Clean up decompiler artifacts in TileVortex files (#117)
Co-authored-by: thehoblit <thehoblit@gmail.com>
2026-05-14 11:29:50 -07:00
Eldrinn-Elantey f4f7e27bca Fix remaining gateway portal texture bugs (#119) 2026-05-14 11:18:30 -07:00
Eldrinn-Elanteyandthehoblit db3f4e0cba Fix vortex flicker on chunk reload and stabilizer sync (#116)
Co-authored-by: thehoblit <thehoblit@gmail.com>
2026-05-14 09:43:40 -07:00
Eldrinn-Elantey 2768e1b1eb Fix player orientation on pocket plane portal exit (#115) 2026-05-14 08:47:30 -07:00
Eldrinn-Elantey 0a54bd5465 Fix pocket plane keystone binding, portal bugs and world saving (#114) 2026-05-14 01:07:38 -04:00
Alexdoru 892f8941e2 Fix two world leaks (#110) 2026-04-22 18:11:09 +00:00
ROBERT-PARKOURCIVILIZATIONGOD 5b04ae1082 Fix NPE in generatePocketPlane during async vortex creation (#111) 2026-04-22 13:03:35 -05:00
Martin Robertz 1dc5f139e5 update+gradle+bs+deps 2026-04-06 11:42:04 +02:00
Ranzu ad2c1bcc5f Corrected guis bounds (#109) 2026-04-05 23:18:54 +02:00
UltraProdigy 8546cab959 Optimize Images & Workflow (#108) 2026-03-16 08:47:43 +00:00
Yoshy f69e39227e fix typos (#105) 2026-01-28 06:25:37 -05:00
302 changed files with 1362 additions and 1271 deletions

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
name: Optimize images
on:
pull_request:
branches: [ master, main, release/** ]
paths:
- "**/*.png"
push:
branches: [ master, main, release/** ]
paths:
- "**/*.png"
jobs:
optimize-images:
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master
secrets: inherit
+2 -2
View File
@@ -1,9 +1,9 @@
// Add your dependencies here
dependencies {
api('com.github.GTNewHorizons:Baubles-Expanded:2.2.2-GTNH:dev')
api('com.github.GTNewHorizons:Baubles-Expanded:2.2.11-GTNH:dev')
api('thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev')
compileOnly('com.github.GTNewHorizons:GT5-Unofficial:5.09.52.69:dev') {transitive = false}
compileOnly('com.github.GTNewHorizons:GT5-Unofficial:5.09.52.409:dev') {transitive = false}
}
+36 -3
View File
@@ -39,9 +39,33 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
developmentEnvironmentUserName = Developer
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
# See https://github.com/bsideup/jabel for details on how this works.
enableModernJavaSyntax = true
# Enables modern Java syntax support. Valid values:
# - false: No modern syntax, Java 8 only
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
# - modern: Native modern Java bytecode, no downgrading
enableModernJavaSyntax = jabel
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
# and should *not* be used in most situations. -1 disables this.
# forceToolchainVersion = -1
# Target JVM version for JVM Downgrader bytecode downgrading.
# Only used when enableModernJavaSyntax = jvmDowngrader
# downgradeTargetVersion = 8
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
# Classes will be available in META-INF/versions/N/ for each version N in this list.
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
# jvmDowngraderMultiReleaseVersions = 21,25
# Specifies how JVM Downgrader API stubs are provided. Options:
# - shade: Shade minimized stubs into the jar
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
# - external: Another dependency provides stubs (no constraint, no warning)
# - (empty): Warning reminding you to configure stubs
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
# jvmDowngraderStubsProvider =
# Enables injecting missing generics into the decompiled source code for a better coding experience.
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
@@ -174,6 +198,12 @@ curseForgeRelations = requiredDependency\:thaumcraft
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
# runClientWorkingDirectory = run/client
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
# runServerWorkingDirectory = run/server
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
@@ -203,3 +233,6 @@ curseForgeRelations = requiredDependency\:thaumcraft
# This is meant to be set in $HOME/.gradle/gradle.properties.
# ideaCheckSpotlessOnBuild = true
# Non-GTNH properties
org.gradle.configuration-cache = true
org.gradle.parallel = true
+12
View File
@@ -0,0 +1,12 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d4fd992c9557644e637ebe98263e0ae7/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410b387cc69ccafb1a74/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect
toolchainVendor=ADOPTIUM
toolchainVersion=25
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+3 -6
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -212,8 +210,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
Vendored
+1 -2
View File
@@ -70,11 +70,10 @@ goto fail
: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 %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.46'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.24'
}
@@ -29,6 +29,7 @@ import net.minecraft.item.Item;
import net.minecraft.world.World;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import com.kentington.thaumichorizons.client.audio.VortexSound;
import com.kentington.thaumichorizons.client.fx.FXSonic;
@@ -166,6 +167,7 @@ import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import thaumcraft.api.wands.IWandTriggerManager;
import thaumcraft.client.fx.ParticleEngine;
import thaumcraft.client.fx.particles.FXBurst;
@@ -184,6 +186,7 @@ public class ClientProxy extends CommonProxy {
@Override
public void registerHandlers() {
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(ThaumicHorizons.instance.renderEventHandler);
final IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
if (resourceManager instanceof IReloadableResourceManager) {
@@ -198,6 +201,7 @@ public class ClientProxy extends CommonProxy {
@Override
public void registerRenderers() {
// spotless:off
ClientRegistry.bindTileEntitySpecialRenderer(TileNodeMonitor.class, new TileNodeMonitorRender());
ClientRegistry.bindTileEntitySpecialRenderer(TileSyntheticNode.class, new TileEtherealShardRender());
ClientRegistry.bindTileEntitySpecialRenderer(TileVisDynamo.class, new TileVisDynamoRender());
@@ -209,8 +213,7 @@ public class ClientProxy extends CommonProxy {
ClientRegistry.bindTileEntitySpecialRenderer(TileVatMatrix.class, new TileVatMatrixRender(0));
ClientRegistry.bindTileEntitySpecialRenderer(TileBloodInfuser.class, new TileBloodInfuserRender());
ClientRegistry.bindTileEntitySpecialRenderer(TileSoulBeacon.class, new TileSoulBeaconRender());
ClientRegistry
.bindTileEntitySpecialRenderer(TileTransductionAmplifier.class, new TileTransductionAmplifierRender());
ClientRegistry.bindTileEntitySpecialRenderer(TileTransductionAmplifier.class, new TileTransductionAmplifierRender());
ClientRegistry.bindTileEntitySpecialRenderer(TileRecombinator.class, new TileRecombinatorRender());
ClientRegistry.bindTileEntitySpecialRenderer(TileVortexStabilizer.class, new TileVortexStabilizerRender());
ClientRegistry.bindTileEntitySpecialRenderer(TileVortex.class, new TileVortexRender());
@@ -220,61 +223,29 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityAlchemitePrimed.class, new RenderAlchemitePrimed());
RenderingRegistry.registerEntityRenderingHandler(EntitySyringe.class, new RenderSyringe());
RenderingRegistry.registerEntityRenderingHandler(EntityBlastPhial.class, new BlastPhialRender());
RenderingRegistry
.registerEntityRenderingHandler(EntityChocolateCow.class, new RenderChocolateCow(new ModelCow(), 0.7f));
RenderingRegistry.registerEntityRenderingHandler(
EntityOrePig.class,
new RenderOreBoar(new ModelPig(), new ModelPig(0.5f), 0.7f));
RenderingRegistry.registerEntityRenderingHandler(
EntityGuardianPanther.class,
new RenderGuardianPanther(new ModelOcelot(), 1.0f));
RenderingRegistry
.registerEntityRenderingHandler(EntityFamiliar.class, new RenderFamiliar(new ModelFamiliar(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(
EntityGravekeeper.class,
new RenderGravekeeper(new ModelOcelot(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(
EntityGoldChicken.class,
new RenderGoldChicken(new ModelChicken(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(
EntityScholarChicken.class,
new RenderScholarChicken(new ModelChicken(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(
EntityTaintPig.class,
new RenderTaintfeeder(new ModelPig(), new ModelPig(0.5f), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(
EntityNetherHound.class,
new RenderNetherHound(new ModelWolf(), new ModelWolf(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(
EntitySeawolf.class,
new RenderSeawolf(new ModelWolf(), new ModelWolf(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(
EntityLunarWolf.class,
new RenderLunarWolf(new ModelWolf(), new ModelWolf(), 0.5f));
RenderingRegistry
.registerEntityRenderingHandler(EntityGolemTH.class, new RenderGolemTH(new ModelGolemTH(false)));
RenderingRegistry
.registerEntityRenderingHandler(EntityEndersteed.class, new RenderEndersteed(new ModelHorse(), 0.75f));
RenderingRegistry
.registerEntityRenderingHandler(EntityNightmare.class, new RenderNightmare(new ModelHorse(), 0.75f));
RenderingRegistry.registerEntityRenderingHandler(EntityChocolateCow.class, new RenderChocolateCow(new ModelCow(), 0.7f));
RenderingRegistry.registerEntityRenderingHandler(EntityOrePig.class, new RenderOreBoar(new ModelPig(), new ModelPig(0.5f), 0.7f));
RenderingRegistry.registerEntityRenderingHandler(EntityGuardianPanther.class, new RenderGuardianPanther(new ModelOcelot(), 1.0f));
RenderingRegistry.registerEntityRenderingHandler(EntityFamiliar.class, new RenderFamiliar(new ModelFamiliar(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(EntityGravekeeper.class, new RenderGravekeeper(new ModelOcelot(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(EntityGoldChicken.class, new RenderGoldChicken(new ModelChicken(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(EntityScholarChicken.class, new RenderScholarChicken(new ModelChicken(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(EntityTaintPig.class, new RenderTaintfeeder(new ModelPig(), new ModelPig(0.5f), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(EntityNetherHound.class, new RenderNetherHound(new ModelWolf(), new ModelWolf(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(EntitySeawolf.class, new RenderSeawolf(new ModelWolf(), new ModelWolf(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(EntityLunarWolf.class, new RenderLunarWolf(new ModelWolf(), new ModelWolf(), 0.5f));
RenderingRegistry.registerEntityRenderingHandler(EntityGolemTH.class, new RenderGolemTH(new ModelGolemTH(false)));
RenderingRegistry.registerEntityRenderingHandler(EntityEndersteed.class, new RenderEndersteed(new ModelHorse(), 0.75f));
RenderingRegistry.registerEntityRenderingHandler(EntityNightmare.class, new RenderNightmare(new ModelHorse(), 0.75f));
RenderingRegistry.registerEntityRenderingHandler(EntityBoatGreatwood.class, new RenderBoatGreatwood());
RenderingRegistry.registerEntityRenderingHandler(EntityBoatThaumium.class, new RenderBoatThaumium());
RenderingRegistry.registerEntityRenderingHandler(
EntityMeatSlime.class,
new RenderMeatSlime(new ModelSlime(16), new ModelSlime(0), 0.25f));
RenderingRegistry.registerEntityRenderingHandler(
EntityMercurialSlime.class,
new RenderMercurialSlime(new ModelSlime(16), new ModelSlime(0), 0.25f));
RenderingRegistry.registerEntityRenderingHandler(
EntityVoltSlime.class,
new RenderVoltSlime(new ModelSlime(16), new ModelSlime(0), 0.25f));
RenderingRegistry.registerEntityRenderingHandler(
EntityMedSlime.class,
new RenderMedSlime(new ModelSlime(16), new ModelSlime(0), 0.25f));
RenderingRegistry.registerEntityRenderingHandler(EntityMeatSlime.class, new RenderMeatSlime(new ModelSlime(16), new ModelSlime(0), 0.25f));
RenderingRegistry.registerEntityRenderingHandler(EntityMercurialSlime.class, new RenderMercurialSlime(new ModelSlime(16), new ModelSlime(0), 0.25f));
RenderingRegistry.registerEntityRenderingHandler(EntityVoltSlime.class, new RenderVoltSlime(new ModelSlime(16), new ModelSlime(0), 0.25f));
RenderingRegistry.registerEntityRenderingHandler(EntityMedSlime.class, new RenderMedSlime(new ModelSlime(16), new ModelSlime(0), 0.25f));
RenderingRegistry.registerEntityRenderingHandler(EntitySheeder.class, new RenderSheeder());
RenderingRegistry.registerEntityRenderingHandler(EntitySoul.class, new RenderSoul());
RenderingRegistry
.registerEntityRenderingHandler(EntityLightningBoltFinite.class, new RenderLightningBoltFinite());
RenderingRegistry.registerEntityRenderingHandler(EntityLightningBoltFinite.class, new RenderLightningBoltFinite());
MinecraftForgeClient.registerItemRenderer(ThaumicHorizons.itemSyringeBloodSample, new ItemSyringeRender());
MinecraftForgeClient.registerItemRenderer(ThaumicHorizons.itemSyringeHuman, new ItemSyringeRender());
MinecraftForgeClient.registerItemRenderer(ThaumicHorizons.itemSyringeEmpty, new ItemSyringeRender());
@@ -282,6 +253,7 @@ public class ClientProxy extends CommonProxy {
MinecraftForgeClient.registerItemRenderer(ThaumicHorizons.itemCorpseEffigy, new ItemCorpseEffigyRender());
MinecraftForgeClient.registerItemRenderer(ThaumicHorizons.itemInjector, new ItemInjectorRender());
MinecraftForgeClient.registerItemRenderer(ThaumicHorizons.itemWandCastingDisposable, new ItemWandRenderer());
// spotless:on
}
@Override
@@ -561,6 +533,14 @@ public class ClientProxy extends CommonProxy {
private final Map<TileCoord, VortexSound> activeVortexSounds = new HashMap<>();
@SubscribeEvent
public void onWorldUnload(WorldEvent.Unload event) {
if (event.world.isRemote) {
activeVortexSounds.clear();
ThaumicHorizons.instance.renderEventHandler.thingsThatSparkle.clear();
}
}
@Override
public void playVortexSound(TileVortex tile) {
if (tile == null || tile.isInvalid() || tile.getWorldObj() == null) return;
@@ -25,8 +25,8 @@ public class GuiCase extends GuiContainer {
final int z) {
super(new ContainerCase(par1InventoryPlayer, world, x, y, z));
this.blockSlot = par1InventoryPlayer.currentItem;
this.xSize = 175;
this.ySize = 232;
this.xSize = 176;
this.ySize = 233;
}
protected void drawGuiContainerForegroundLayer(final int par1, final int par2) {
@@ -22,7 +22,7 @@ public class GuiInspiratron extends GuiContainer {
public GuiInspiratron(final InventoryPlayer player, final TileInspiratron tile) {
super(new ContainerInspiratron(player, tile));
this.tile = tile;
this.xSize = 175;
this.xSize = 176;
this.ySize = 219;
}
@@ -22,8 +22,8 @@ public class GuiSoulExtractor extends GuiContainer {
public GuiSoulExtractor(final InventoryPlayer player, final TileSoulExtractor tile) {
super(new ContainerSoulExtractor(player, tile));
this.tile = tile;
this.xSize = 175;
this.ySize = 165;
this.xSize = 176;
this.ySize = 166;
}
protected void drawGuiContainerBackgroundLayer(final float p_146976_1_, final int p_146976_2_,
@@ -14,6 +14,7 @@ import org.lwjgl.opengl.GL11;
import com.kentington.thaumichorizons.common.container.ContainerVat;
import com.kentington.thaumichorizons.common.lib.EntityInfusionProperties;
import com.kentington.thaumichorizons.common.tiles.TileVat;
import com.kentington.thaumichorizons.common.tiles.TileVatMatrix;
import thaumcraft.client.lib.UtilsFX;
import thaumcraft.common.lib.research.ResearchManager;
@@ -62,7 +63,9 @@ public class GuiVat extends GuiContainer {
}
}
} else if (this.tile.mode == 4 || this.tile.mode == 2) {
final float adjustedSelfInfusionHealth = this.tile.selfInfusionHealth / 2.0f;
final TileVatMatrix matrix = this.tile.getMatrix();
final float selfInfusionHealth = (matrix != null) ? matrix.selfInfusionHealth : 20f;
final float adjustedSelfInfusionHealth = selfInfusionHealth / 2.0f;
final float max2 = 10.0f;
for (int j = 0; j < (int) max2; ++j) {
int x = var5 + 56 + 7 * j - 63 * (j / 9);
@@ -102,11 +102,11 @@ public class TileVatMatrixRender extends TileEntitySpecialRenderer {
float instability = 0.0f;
float startUp = 0.0f;
float craftCount = 0.0f;
if (vat != null) {
startUp = vat.startUp;
instability = vat.instability;
craftCount = vat.craftCount;
if (tile.crafting) {
startUp = 1.0f;
}
instability = tile.instability;
craftCount = tile.craftCount;
if (tile.getWorldObj() != null) {
GL11.glRotatef(ticks % 360.0f * startUp, 0.0f, 1.0f, 0.0f);
}
@@ -202,8 +202,8 @@ public class TileVatMatrixRender extends TileEntitySpecialRenderer {
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glPopMatrix();
GL11.glPopMatrix();
if (vat != null && vat.mode == 2) {
this.drawHalo(vat, par2, par4, par6, par8, vat.craftCount);
if (tile.crafting) {
this.drawHalo(vat, par2, par4, par6, par8, tile.craftCount);
}
}
@@ -74,7 +74,7 @@ public class TileVatSlaveRender extends TileEntitySpecialRenderer {
(float) z + 0.5f);
UtilsFX.bindTexture("thaumichorizons", TileVatSlaveRender.tx1);
this.corpse.render(null, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.1f);
} else if (tco.mode == 4 || (tco.mode == 2 && tco.recipeType == 1)) {
} else if (tco.mode == 4) {
GL11.glRotatef(180.0f, 0.0f, 0.0f, 1.0f);
GL11.glTranslatef(
(float) (-x) - 0.5f,
@@ -7,13 +7,12 @@ package com.kentington.thaumichorizons.client.renderer.tile;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Vec3;
import org.lwjgl.opengl.GL11;
@@ -23,7 +22,6 @@ import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import thaumcraft.api.aspects.Aspect;
import thaumcraft.api.aspects.AspectList;
import thaumcraft.client.lib.QuadHelper;
import thaumcraft.client.lib.UtilsFX;
@SideOnly(Side.CLIENT)
@@ -34,25 +32,20 @@ public class TileVortexRender extends TileEntitySpecialRenderer {
public void renderTileEntityAt(final TileEntity tile, final double x, final double y, final double z,
final float partialTicks) {
if (!(tile instanceof final TileVortex node)) {
if (!(tile instanceof final TileVortex node) || !node.clientSynced) {
return;
}
final float size = 10.0f;
final double viewDistance = 64.0;
final EntityLivingBase viewer = Minecraft.getMinecraft().renderViewEntity;
final boolean condition = true;
final boolean depthIgnore = false;
renderNode(
viewer,
viewDistance,
condition,
depthIgnore,
size,
Minecraft.getMinecraft().renderViewEntity,
64.0,
true,
false,
10.0f,
tile.xCoord,
tile.yCoord,
tile.zCoord,
partialTicks,
((TileVortex) tile).aspects,
node.aspects,
node.count,
node.collapsing,
node.beams,
@@ -72,6 +65,10 @@ public class TileVortexRender extends TileEntitySpecialRenderer {
return;
}
float alpha = (float) ((viewDistance - distance) / viewDistance);
// Camera-relative center — avoids float precision loss at large world coords.
final double cx = x + 0.5 - RenderManager.renderPosX;
final double cy = y + 0.5 - RenderManager.renderPosY;
final double cz = z + 0.5 - RenderManager.renderPosZ;
GL11.glPushMatrix();
GL11.glAlphaFunc(GL11.GL_GREATER, 0.003921569f);
GL11.glDepthMask(false);
@@ -79,7 +76,6 @@ public class TileVortexRender extends TileEntitySpecialRenderer {
GL11.glDisable(GL11.GL_DEPTH_TEST);
}
GL11.glDisable(GL11.GL_CULL_FACE);
final long time = nt / 5000000L;
final float bscale = 0.25f;
GL11.glPushMatrix();
final float rad = ((float) Math.PI * 2F);
@@ -101,10 +97,10 @@ public class TileVortexRender extends TileEntitySpecialRenderer {
GL11.glPushMatrix();
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, aspect.getBlend());
scale = MathHelper.sin(viewer.ticksExisted / (14.0f - count)) * bscale + bscale * 2.0f;
scale = 0.4f;
scale *= size;
angle = time % (5000 + 500 * count) / (5000.0f + 500 * count) * rad;
final long periodNs = (5000L + 500L * count) * 5_000_000L;
angle = (float) ((double) (nt % periodNs) / periodNs) * rad;
if (beams < 6 || timeOpen < 50) {
UtilsFX.renderFacingStrip(
x + 0.5,
@@ -153,9 +149,9 @@ public class TileVortexRender extends TileEntitySpecialRenderer {
if (!cheat && (beams < 6 || timeOpen < 50)) {
UtilsFX.bindTexture(TileVortexRender.vortextex);
renderVortex(
x + 0.5,
y + 0.5,
z + 0.5,
cx,
cy,
cz,
angle * 20.0f * corescale / (1 + 2 * beams),
scale / 5.0f * corescale,
0.8f,
@@ -190,10 +186,10 @@ public class TileVortexRender extends TileEntitySpecialRenderer {
GL11.glPushMatrix();
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
scale = MathHelper.sin(viewer.ticksExisted / (14.0f - count)) * bscale + bscale * 2.0f;
scale = 0.4f;
scale *= size;
angle = time % (5000 + 500 * count) / (5000.0f + 500 * count) * rad;
final long periodNs2 = (5000L + 500L * count) * 5_000_000L;
angle = (float) ((double) (nt % periodNs2) / periodNs2) * rad;
UtilsFX.renderFacingStrip(
x + 0.5,
y + 0.5,
@@ -233,37 +229,29 @@ public class TileVortexRender extends TileEntitySpecialRenderer {
final float arYZ = ActiveRenderInfo.rotationYZ;
final float arXY = ActiveRenderInfo.rotationXY;
final float arXZ = ActiveRenderInfo.rotationXZ;
final EntityPlayer player = (EntityPlayer) Minecraft.getMinecraft().renderViewEntity;
final double iPX = player.prevPosX + (player.posX - player.prevPosX) * partialTicks;
final double iPY = player.prevPosY + (player.posY - player.prevPosY) * partialTicks;
final double iPZ = player.prevPosZ + (player.posZ - player.prevPosZ) * partialTicks;
GL11.glTranslated(-iPX, -iPY, -iPZ);
// Rotate screen-plane basis vectors (H = right, V = up) by angle.
// This avoids Rodrigues rotation around a qvec that shifts with every head movement,
// which caused the vortex to jerk whenever the player moved the camera.
final float cos = MathHelper.cos(angle);
final float sin = MathHelper.sin(angle);
final float hX = cos * arX + sin * arYZ;
final float hY = sin * arXZ;
final float hZ = cos * arZ + sin * arXY;
final float vX = cos * arYZ - sin * arX;
final float vY = cos * arXZ;
final float vZ = cos * arXY - sin * arZ;
tessellator.startDrawingQuads();
tessellator.setBrightness(220);
tessellator.setColorRGBA_I(color, (int) (alpha * 255.0f));
final Vec3 v1 = Vec3
.createVectorHelper(-arX * scale - arYZ * scale, -arXZ * scale, -arZ * scale - arXY * scale);
final Vec3 v2 = Vec3.createVectorHelper(-arX * scale + arYZ * scale, arXZ * scale, -arZ * scale + arXY * scale);
final Vec3 v3 = Vec3.createVectorHelper(arX * scale + arYZ * scale, arXZ * scale, arZ * scale + arXY * scale);
final Vec3 v4 = Vec3.createVectorHelper(arX * scale - arYZ * scale, -arXZ * scale, arZ * scale - arXY * scale);
if (angle != 0.0f) {
final Vec3 pvec = Vec3.createVectorHelper(iPX, iPY, iPZ);
final Vec3 tvec = Vec3.createVectorHelper(px, py, pz);
final Vec3 qvec = pvec.subtract(tvec).normalize();
QuadHelper.setAxis(qvec, angle).rotate(v1);
QuadHelper.setAxis(qvec, angle).rotate(v2);
QuadHelper.setAxis(qvec, angle).rotate(v3);
QuadHelper.setAxis(qvec, angle).rotate(v4);
}
final float f2 = 0.0f;
final float f3 = 1.0f;
final float f4 = 0.0f;
final float f5 = 1.0f;
tessellator.setNormal(0.0f, 0.0f, -1.0f);
tessellator.addVertexWithUV(px + v1.xCoord, py + v1.yCoord, pz + v1.zCoord, f2, f5);
tessellator.addVertexWithUV(px + v2.xCoord, py + v2.yCoord, pz + v2.zCoord, f3, f5);
tessellator.addVertexWithUV(px + v3.xCoord, py + v3.yCoord, pz + v3.zCoord, f3, f4);
tessellator.addVertexWithUV(px + v4.xCoord, py + v4.yCoord, pz + v4.zCoord, f2, f4);
tessellator
.addVertexWithUV(px + (-hX - vX) * scale, py + (-hY - vY) * scale, pz + (-hZ - vZ) * scale, 0.0, 1.0);
tessellator
.addVertexWithUV(px + (-hX + vX) * scale, py + (-hY + vY) * scale, pz + (-hZ + vZ) * scale, 1.0, 1.0);
tessellator.addVertexWithUV(px + (hX + vX) * scale, py + (hY + vY) * scale, pz + (hZ + vZ) * scale, 1.0, 0.0);
tessellator.addVertexWithUV(px + (hX - vX) * scale, py + (hY - vY) * scale, pz + (hZ - vZ) * scale, 0.0, 0.0);
tessellator.draw();
}
@@ -117,7 +117,7 @@ public class BlockGatewayPortal extends Block {
}
}
final TileEntity te = world.getTileEntity(slotX, slotY, slotZ);
if (te instanceof TileSlot) {
if (te instanceof TileSlot && ((TileSlot) te).portalOpen) {
((TileSlot) te).destroyPortal();
}
}
@@ -232,78 +232,72 @@ public class BlockGatewayPortal extends Block {
}
case 5 -> {
switch (side) {
case 0: {
case 0 -> {
return this.lapiz;
}
case 1: {
case 1 -> {
return this.stone;
}
case 2: {
if (isXAligned) {
return this.topR;
}
case 2 -> {
if (isXAligned) return this.topR;
return this.stone;
}
case 3: {
if (isXAligned) {
return this.topL;
}
case 3 -> {
if (isXAligned) return this.topL;
return this.stone;
}
case 4: {
if (!isXAligned) {
return this.topL;
}
case 4 -> {
if (!isXAligned) return this.topL;
return this.stone;
}
case 5: {
if (!isXAligned) {
return this.topR;
}
break;
case 5 -> {
if (!isXAligned) return this.topR;
return this.stone;
}
default -> {
return this.lapiz;
}
}
return this.lapiz;
}
case 6, 7, 9 -> {
return this.bottomSide(isXAligned, side);
}
case 8 -> {
switch (side) {
case 0: {
case 0 -> {
return this.lapiz;
}
case 1: {
case 1 -> {
return this.stone;
}
case 2: {
if (isXAligned) {
return this.topL;
}
case 2 -> {
if (isXAligned) return this.topL;
return this.stone;
}
case 3: {
if (isXAligned) {
return this.topR;
}
case 3 -> {
if (isXAligned) return this.topR;
return this.stone;
}
case 4: {
if (!isXAligned) {
return this.topR;
}
case 4 -> {
if (!isXAligned) return this.topR;
return this.stone;
}
case 5: {
if (!isXAligned) {
return this.topL;
}
break;
case 5 -> {
if (!isXAligned) return this.topL;
return this.stone;
}
default -> {
return this.lapiz;
}
}
return this.lapiz;
}
case 10 -> {
switch (side) {
case 0 -> {
return this.stone;
return this.lapiz;
}
case 1 -> {
return this.lapiz;
return this.stone;
}
case 2 -> {
if (isXAligned) {
@@ -14,6 +14,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import com.kentington.thaumichorizons.common.ThaumicHorizons;
import com.kentington.thaumichorizons.common.lib.GatewayTeleporter;
@@ -83,12 +84,12 @@ public class BlockPortalTH extends BlockBreakable {
public void onEntityCollidedWithBlock(final World world, final int x, final int y, final int z,
final Entity player) {
if (player.ridingEntity == null && player.riddenByEntity == null && player instanceof EntityPlayerMP) {
if (player.timeUntilPortal > 0) {
player.timeUntilPortal = 10;
if (player.ridingEntity == null && player.riddenByEntity == null && player instanceof final EntityPlayerMP playerMP) {
if (playerMP.timeUntilPortal > 0) {
playerMP.timeUntilPortal = 10;
return;
}
player.timeUntilPortal = 10;
playerMP.timeUntilPortal = 10;
int targetX = 0;
int targetY = 0;
int targetZ = 0;
@@ -116,15 +117,21 @@ public class BlockPortalTH extends BlockBreakable {
if (portal.length > 3) targetDim = portal[3];
final MinecraftServer mServer = FMLCommonHandler.instance().getMinecraftServerInstance();
((EntityPlayerMP) player).mcServer.getConfigurationManager().transferPlayerToDimension(
(EntityPlayerMP) player,
final WorldServer targetWorld = mServer.worldServerForDimension(targetDim);
float exitYaw = playerMP.rotationYaw;
// face perpendicular to portal plane, away from portal into the world
if (targetWorld.getTileEntity(targetX, targetY + 2, targetZ) instanceof final TileSlot slotTE) {
exitYaw = slotTE.xAligned ? 0.0f : 270.0f;
}
playerMP.mcServer.getConfigurationManager().transferPlayerToDimension(
playerMP,
targetDim,
new GatewayTeleporter(
mServer.worldServerForDimension(ThaumicHorizons.dimensionPocketId),
targetWorld,
targetX,
targetY,
targetZ,
player.rotationYaw));
exitYaw));
} else {
int slotY = y;
int slotX = x;
@@ -167,8 +174,8 @@ public class BlockPortalTH extends BlockBreakable {
}
}
final MinecraftServer mServer2 = FMLCommonHandler.instance().getMinecraftServerInstance();
((EntityPlayerMP) player).mcServer.getConfigurationManager().transferPlayerToDimension(
(EntityPlayerMP) player,
playerMP.mcServer.getConfigurationManager().transferPlayerToDimension(
playerMP,
ThaumicHorizons.dimensionPocketId,
new GatewayTeleporter(
mServer2.worldServerForDimension(ThaumicHorizons.dimensionPocketId),
@@ -32,6 +32,7 @@ public class BlockSlot extends BlockContainer {
public void breakBlock(final World world, final int x, final int y, final int z, final Block block, final int md) {
final TileSlot tco = (TileSlot) world.getTileEntity(x, y, z);
if (tco == null) return;
if (tco.portalOpen) {
tco.destroyPortal();
}
@@ -59,15 +60,20 @@ public class BlockSlot extends BlockContainer {
if (tco.portalOpen) {
tco.destroyPortal();
}
world.markBlockForUpdate(x, y, z);
return true;
} else if (!tco.portalOpen && player.getHeldItem().getItem() instanceof ItemWandCasting) {
tco.makePortal(player);
world.markBlockForUpdate(x, y, z);
return true;
}
} else if (theItem != null && theItem.getItem() == ThaumicHorizons.itemKeystone
&& theItem.stackTagCompound != null) {
tco.insertKeystone(theItem.stackTagCompound.getInteger("dimension"));
--theItem.stackSize;
world.markBlockForUpdate(x, y, z);
return true;
}
world.markBlockForUpdate(x, y, z);
return false;
}
@@ -16,6 +16,7 @@ import net.minecraft.world.World;
import com.kentington.thaumichorizons.common.ThaumicHorizons;
import com.kentington.thaumichorizons.common.tiles.TileVat;
import com.kentington.thaumichorizons.common.tiles.TileVatConnector;
import com.kentington.thaumichorizons.common.tiles.TileVatMatrix;
import com.kentington.thaumichorizons.common.tiles.TileVatSlave;
import cpw.mods.fml.relauncher.Side;
@@ -65,7 +66,12 @@ public class BlockVat extends BlockContainer {
public void breakBlock(final World world, final int x, final int y, final int z, final Block block, final int md) {
if (md == 7) {
((TileVat) world.getTileEntity(x, y, z)).killMe();
final TileVat vat = (TileVat) world.getTileEntity(x, y, z);
vat.killMe();
final net.minecraft.tileentity.TileEntity above = world.getTileEntity(x, y + 1, z);
if (above instanceof TileVatMatrix) {
((TileVatMatrix) above).onInfusionInterrupted();
}
} else {
((TileVatSlave) world.getTileEntity(x, y, z)).killMyBoss(md);
}
@@ -50,8 +50,11 @@ public class ItemKeystone extends Item {
public void addInformation(final ItemStack par1ItemStack, final EntityPlayer par2EntityPlayer, final List par3List,
final boolean par4) {
if (par1ItemStack.getTagCompound() != null && par1ItemStack.getTagCompound().getInteger("dimension") != 0) {
par3List.add(PocketPlaneData.planes.get(par1ItemStack.getTagCompound().getInteger("dimension")).name);
if (par1ItemStack.getTagCompound() != null) {
final int dim = par1ItemStack.getTagCompound().getInteger("dimension");
if (dim >= 0 && dim < PocketPlaneData.planes.size()) {
par3List.add(PocketPlaneData.planes.get(dim).name);
}
}
super.addInformation(par1ItemStack, par2EntityPlayer, par3List, par4);
}
@@ -71,9 +74,13 @@ public class ItemKeystone extends Item {
float hitX, float hitY, float hitZ) {
if (stack.getTagCompound() == null && player.dimension == ThaumicHorizons.dimensionPocketId
&& !world.isRemote) {
if (world.getTileEntity(x, y, z) instanceof TileVortex)
(stack.stackTagCompound = new NBTTagCompound()).setInteger("dimension", (z + 128) / 256);
return true;
if (world.getTileEntity(x, y, z) instanceof final TileVortex vortex) {
stack.stackTagCompound = new NBTTagCompound();
stack.stackTagCompound.setInteger("dimension", vortex.dimensionID);
player.inventory.markDirty();
return true;
}
return false;
}
return super.onItemUseFirst(stack, player, world, x, y, z, side, hitX, hitY, hitZ);
}
@@ -171,7 +171,9 @@ public class EventHandlerEntity {
final PotionEffect effect = new PotionEffect(Potion.resistance.id, Integer.MAX_VALUE, 0, true);
effect.setCurativeItems(new ArrayList<>());
entity.addPotionEffect(effect);
ThaumicHorizons.instance.renderEventHandler.thingsThatSparkle.add(entity);
if (entity.worldObj.isRemote) {
ThaumicHorizons.instance.renderEventHandler.thingsThatSparkle.add(entity);
}
} else if (infusions[i] == 8 && !entity.getEntityData().hasKey("runicCharge")) {
entity.getEntityData().setInteger("runicCharge", 6);
} else if (infusions[i] == 7) {
@@ -32,6 +32,7 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.potion.Potion;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeGenBase;
@@ -111,7 +112,11 @@ public class PocketPlaneData {
}
}
world.setBlock(xCenter, yCenter + 1, zCenter, ThaumicHorizons.blockVortex);
final TileVortex vortex = (TileVortex) world.getTileEntity(xCenter, yCenter + 1, zCenter);
TileEntity te = world.getTileEntity(xCenter, yCenter + 1, zCenter);
if (!(te instanceof TileVortex)) {
te = new TileVortex();
}
final TileVortex vortex = (TileVortex) te;
vortex.cheat = true;
vortex.returnID = returnID;
vortex.dimensionID = PocketPlaneData.planes.size();
@@ -123,7 +128,7 @@ public class PocketPlaneData {
data.portalD = new int[4];
PocketPlaneData.planes.add(data);
PocketPlaneData.positions.put(pocketPlaneMAXID, Vec3.createVectorHelper(vortexX, vortexY, vortexZ));
world.getChunkFromBlockCoords(vortexX, vortexZ).isModified = true;
world.getChunkFromBlockCoords(xCenter, zCenter).isModified = true;
creatures = 0;
++pocketPlaneMAXID;
}
@@ -4,12 +4,11 @@
package com.kentington.thaumichorizons.common.lib.networking;
import java.util.HashMap;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ChunkCoordinates;
import com.kentington.thaumichorizons.common.tiles.TileVat;
import com.kentington.thaumichorizons.common.tiles.TileVatMatrix;
import com.kentington.thaumichorizons.common.tiles.TileVatSlave;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
@@ -81,13 +80,17 @@ public class PacketInfusionFX implements IMessage, IMessageHandler<PacketInfusio
if (is == null) {
return null;
}
if (is.sourceFX.containsKey(key)) {
final TileVat.SourceFX sf = is.sourceFX.get(key);
final TileVatMatrix matrix = is.getMatrix();
if (matrix == null) {
return null;
}
if (matrix.sourceFX.containsKey(key)) {
final TileVatMatrix.SourceFX sf = matrix.sourceFX.get(key);
sf.ticks = count;
is.sourceFX.put(key, sf);
matrix.sourceFX.put(key, sf);
} else {
final HashMap<String, TileVat.SourceFX> sourceFX = is.sourceFX;
sourceFX.put(key, new TileVat.SourceFX(new ChunkCoordinates(tx, ty, tz), count, message.color));
matrix.sourceFX
.put(key, new TileVatMatrix.SourceFX(new ChunkCoordinates(tx, ty, tz), count, message.color));
}
}
return null;
File diff suppressed because it is too large. Load diff
@@ -1,93 +1,195 @@
//
// Decompiled by Procyon v0.5.30
//
package com.kentington.thaumichorizons.common.tiles;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
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.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagInt;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ChunkCoordinates;
import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.kentington.thaumichorizons.common.ThaumicHorizons;
import com.kentington.thaumichorizons.common.entities.IEntityInfusedStats;
import com.kentington.thaumichorizons.common.lib.CreatureInfusionRecipe;
import com.kentington.thaumichorizons.common.lib.EntityInfusionProperties;
import com.kentington.thaumichorizons.common.lib.SelfInfusionRecipe;
import com.kentington.thaumichorizons.common.lib.networking.PacketFXEssentiaBubble;
import com.kentington.thaumichorizons.common.lib.networking.PacketFXInfusionDone;
import com.kentington.thaumichorizons.common.lib.networking.PacketHandler;
import com.kentington.thaumichorizons.common.lib.networking.PacketInfusionFX;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.EntityRegistry;
import thaumcraft.api.ThaumcraftApiHelper;
import thaumcraft.api.aspects.Aspect;
import thaumcraft.api.aspects.AspectList;
import thaumcraft.api.aspects.IAspectContainer;
import thaumcraft.api.aspects.IEssentiaTransport;
import thaumcraft.api.crafting.IInfusionStabiliser;
import thaumcraft.api.crafting.InfusionRecipe;
import thaumcraft.api.visnet.TileVisNode;
import thaumcraft.api.visnet.VisNetHandler;
import thaumcraft.api.wands.IWandable;
import thaumcraft.common.Thaumcraft;
import thaumcraft.common.config.Config;
import thaumcraft.common.config.ConfigBlocks;
import thaumcraft.common.lib.network.fx.PacketFXBlockZap;
import thaumcraft.common.lib.utils.InventoryUtils;
import thaumcraft.common.tiles.TilePedestal;
public class TileVatMatrix extends TileVisNode implements IWandable, IAspectContainer {
public class TileVatMatrix extends TileVisNode implements IWandable {
// infusion state
public boolean crafting = false;
public boolean active = false;
public boolean checkSurroundings = true;
public int symmetry = 0;
public int instability = 0;
public AspectList recipeEssentia = new AspectList();
public ArrayList<ItemStack> recipeIngredients = null;
public Object recipeOutput = null;
public String recipePlayer = null;
public String recipeOutputLabel = "";
public int recipeInstability = 0;
public int recipeType = 0;
public float selfInfusionHealth = 20f;
public ArrayList<ChunkCoordinates> pedestals = new ArrayList<>();
public int count = 0;
private final int countDelay = 10;
public int craftCount = 0;
private int itemCount = 0;
private AspectList myEssentiaCost = new AspectList();
public HashMap<String, SourceFX> sourceFX = new HashMap<>();
public TileVat getVat() {
if (this.worldObj == null) {
return null;
}
if (this.worldObj == null) return null;
final TileEntity t = this.worldObj.getTileEntity(this.xCoord, this.yCoord - 1, this.zCoord);
return (t instanceof TileVat) ? ((TileVat) t) : null;
return (t instanceof TileVat) ? (TileVat) t : null;
}
@Override
public AspectList getAspects() {
final TileVat t = this.getVat();
if (t != null) {
return t.getAspects();
public void updateEntity() {
super.updateEntity(); // TileVisNode vis network tick
if (worldObj.isRemote) {
doEffects();
return;
}
++count;
if (checkSurroundings) {
checkSurroundings = false;
getSurroundings();
}
if (active && crafting && count % countDelay == 0) {
craftCycle();
markDirty();
}
return null;
}
@Override
public void setAspects(final AspectList aspects) {}
// -------------------------------------------------------------------------
// Pedestal / symmetry scanning
// -------------------------------------------------------------------------
@Override
public boolean doesContainerAccept(final Aspect tag) {
return false;
public void getSurroundings() {
final ArrayList<ChunkCoordinates> stuff = new ArrayList<>();
pedestals.clear();
try {
for (int xx = -12; xx <= 12; ++xx) {
for (int zz = -12; zz <= 12; ++zz) {
boolean skip = false;
for (int yy = -5; yy <= 10; ++yy) {
if (xx == 0 && zz == 0) continue;
final int x = xCoord + xx, y = yCoord - yy, z = zCoord + zz;
final TileEntity te = worldObj.getTileEntity(x, y, z);
if (!skip && yy > 0 && Math.abs(xx) <= 8 && Math.abs(zz) <= 8 && te instanceof TilePedestal) {
pedestals.add(new ChunkCoordinates(x, y, z));
skip = true;
} else {
final Block bi = worldObj.getBlock(x, y, z);
if (bi == Blocks.skull || (bi instanceof IInfusionStabiliser
&& ((IInfusionStabiliser) bi).canStabaliseInfusion(worldObj, x, y, z))) {
stuff.add(new ChunkCoordinates(x, y, z));
}
}
}
}
}
symmetry = 0;
for (final ChunkCoordinates cc : pedestals) {
boolean items = false;
final int dx = xCoord - cc.posX, dz = zCoord - cc.posZ;
TileEntity te2 = worldObj.getTileEntity(cc.posX, cc.posY, cc.posZ);
if (te2 instanceof TilePedestal) {
symmetry += 2;
if (((TilePedestal) te2).getStackInSlot(0) != null) {
++symmetry;
items = true;
}
}
te2 = worldObj.getTileEntity(xCoord + dx, cc.posY, zCoord + dz);
if (te2 instanceof TilePedestal) {
symmetry -= 2;
if (((TilePedestal) te2).getStackInSlot(0) == null || !items) continue;
--symmetry;
}
}
float sym = 0f;
for (final ChunkCoordinates cc2 : stuff) {
final int dx = xCoord - cc2.posX, dz = zCoord - cc2.posZ;
Block bi2 = worldObj.getBlock(cc2.posX, cc2.posY, cc2.posZ);
if (bi2 == Blocks.skull || (bi2 instanceof IInfusionStabiliser
&& ((IInfusionStabiliser) bi2).canStabaliseInfusion(worldObj, cc2.posX, cc2.posY, cc2.posZ)))
sym += 0.1f;
bi2 = worldObj.getBlock(xCoord + dx, cc2.posY, zCoord + dz);
if (bi2 == Blocks.skull || (bi2 instanceof IInfusionStabiliser
&& ((IInfusionStabiliser) bi2).canStabaliseInfusion(worldObj, cc2.posX, cc2.posY, cc2.posZ)))
sym -= 0.2f;
}
symmetry += (int) sym;
} catch (Exception ignored) {}
}
@Override
public int addToContainer(final Aspect tag, final int amount) {
return 0;
}
// -------------------------------------------------------------------------
// Wand interaction
// -------------------------------------------------------------------------
@Override
public boolean takeFromContainer(final Aspect tag, final int amount) {
return false;
}
@Override
public boolean takeFromContainer(final AspectList ot) {
return false;
}
@Override
public boolean doesContainerContainAmount(final Aspect tag, final int amount) {
return false;
}
@Override
public boolean doesContainerContain(final AspectList ot) {
return false;
}
@Override
public int containerContains(final Aspect tag) {
return 0;
public ItemStack onWandRightClick(final World world, final ItemStack wandstack, final EntityPlayer player) {
final TileVat vat = getVat();
if (vat != null && !crafting && ((vat.isValidInfusionTarget() && vat.mode == 0) || vat.mode == 4)) {
craftingStart(player);
}
return wandstack;
}
@Override
public int onWandRightClick(final World world, final ItemStack wandstack, final EntityPlayer player, final int x,
final int y, final int z, final int side, final int md) {
this.onWandRightClick(world, wandstack, player);
onWandRightClick(world, wandstack, player);
return 0;
}
@Override
public ItemStack onWandRightClick(final World world, final ItemStack wandstack, final EntityPlayer player) {
final TileVat t = this.getVat();
if (t != null && ((t.isValidInfusionTarget() && t.mode == 0) || t.mode == 4)) {
t.startInfusion(player);
}
return wandstack;
}
@Override
public void onUsingWandTick(final ItemStack wandstack, final EntityPlayer player, final int count) {}
@@ -95,6 +197,715 @@ public class TileVatMatrix extends TileVisNode implements IWandable, IAspectCont
public void onWandStoppedUsing(final ItemStack wandstack, final World world, final EntityPlayer player,
final int count) {}
// -------------------------------------------------------------------------
// Infusion start
// -------------------------------------------------------------------------
public void craftingStart(final EntityPlayer player) {
final TileVat vat = getVat();
if (vat == null) return;
getSurroundings();
final ArrayList<ItemStack> components = new ArrayList<>();
for (final ChunkCoordinates cc : pedestals) {
final TileEntity te = worldObj.getTileEntity(cc.posX, cc.posY, cc.posZ);
if (te instanceof TilePedestal) {
final ItemStack s = ((TilePedestal) te).getStackInSlot(0);
if (s != null) components.add(s.copy());
}
}
if (components.isEmpty()) return;
if (vat.mode != 4) {
final CreatureInfusionRecipe recipe = ThaumicHorizons
.getCreatureInfusion(vat.getEntityContained(), components, player);
if (recipe == null) return;
if (recipe.getID(null) != 0
&& ((EntityInfusionProperties) vat.getEntityContained().getExtendedProperties("CreatureInfusion"))
.hasInfusion(recipe.getID(null)))
return;
if (recipe.getRecipeOutput() instanceof NBTTagCompound
&& ((NBTTagCompound) recipe.getRecipeOutput()).getInteger("instilledLoyalty") != 0
&& ((EntityLiving) vat.getEntityContained()).tasks.taskEntries.isEmpty())
return;
recipeType = 0;
recipeIngredients = new ArrayList<>();
for (final ItemStack ing : recipe.getComponents()) recipeIngredients.add(ing.copy());
final Object out = recipe.getRecipeOutput(vat.getEntityContained().getClass());
if (out instanceof Object[]) {
recipeOutputLabel = (String) ((Object[]) out)[0];
recipeOutput = ((Object[]) out)[1];
} else {
recipeOutput = out;
}
recipeInstability = recipe.getInstability(vat.getEntityContained().getClass());
recipeEssentia = recipe.getAspects(vat.getEntityContained().getClass()).copy();
} else {
final SelfInfusionRecipe recipe = ThaumicHorizons.getSelfInfusion(components, player);
if (recipe == null) return;
for (final int id : vat.selfInfusions) {
if (id == recipe.getID()) return;
}
recipeType = 1;
recipeIngredients = new ArrayList<>();
for (final ItemStack ing : recipe.getComponents()) recipeIngredients.add(ing.copy());
recipeOutputLabel = "";
recipeOutput = recipe.getID();
recipeInstability = recipe.getInstability();
recipeEssentia = recipe.getAspects().copy();
}
myEssentiaCost = recipeEssentia.copy();
recipePlayer = player.getCommandSenderName();
instability = symmetry + recipeInstability;
active = true;
crafting = true;
worldObj.playSoundEffect(xCoord, yCoord, zCoord, "thaumcraft:craftstart", 0.5f, 1.0f);
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
}
// -------------------------------------------------------------------------
// Essentia drawing
// -------------------------------------------------------------------------
private boolean tryDrawAllEssentia() {
final TileVat vat = getVat();
if (vat == null) return false;
final int vx = vat.xCoord, vy = vat.yCoord, vz = vat.zCoord;
boolean drew = false;
TileEntity conn;
conn = worldObj.getTileEntity(vx - 1, vy - 3, vz);
if (conn instanceof TileVatConnector) drew |= tryDrawEssentia((TileVatConnector) conn);
if (drew) return true;
conn = worldObj.getTileEntity(vx + 1, vy - 3, vz);
if (conn instanceof TileVatConnector) drew |= tryDrawEssentia((TileVatConnector) conn);
if (drew) return true;
conn = worldObj.getTileEntity(vx, vy - 3, vz - 1);
if (conn instanceof TileVatConnector) drew |= tryDrawEssentia((TileVatConnector) conn);
if (drew) return true;
conn = worldObj.getTileEntity(vx, vy - 3, vz + 1);
if (conn instanceof TileVatConnector) drew |= tryDrawEssentia((TileVatConnector) conn);
return drew;
}
private boolean tryDrawEssentia(final TileVatConnector conn) {
for (final ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
final TileEntity te = ThaumcraftApiHelper
.getConnectableTile(worldObj, conn.xCoord, conn.yCoord, conn.zCoord, dir);
if (te == null) continue;
final IEssentiaTransport ic = (IEssentiaTransport) te;
if (ic.getEssentiaAmount(dir.getOpposite()) > 0 && ic.getSuctionAmount(dir.getOpposite()) < 128
&& ic.getMinimumSuction() <= 128) {
for (final Aspect asp : recipeEssentia.getAspects()) {
if (recipeEssentia.getAmount(asp) > 0) {
final int got = ic.takeEssentia(asp, 1, dir.getOpposite());
if (got > 0) {
recipeEssentia.reduce(asp, got);
clientEssentiaFX(asp);
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
return true;
}
}
}
}
}
return false;
}
private void clientEssentiaFX(final Aspect asp) {
PacketHandler.INSTANCE.sendToAllAround(
new PacketFXEssentiaBubble(xCoord + 0.5, yCoord - 3, zCoord + 0.5, asp.getColor()),
new NetworkRegistry.TargetPoint(worldObj.provider.dimensionId, xCoord, yCoord, zCoord, 32.0));
}
// -------------------------------------------------------------------------
// Instability events
// -------------------------------------------------------------------------
private void inEvZap(final boolean all) {
final List<EntityLivingBase> targets = worldObj.getEntitiesWithinAABB(
EntityLivingBase.class,
AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord, xCoord + 1, yCoord + 1, zCoord + 1)
.expand(10, 10, 10));
if (targets == null || targets.isEmpty()) return;
for (final Entity t : targets) {
thaumcraft.common.lib.network.PacketHandler.INSTANCE.sendToAllAround(
new PacketFXBlockZap(
xCoord + 0.5f,
yCoord + 0.5f,
zCoord + 0.5f,
(float) t.posX,
(float) t.posY + t.height / 2f,
(float) t.posZ),
new NetworkRegistry.TargetPoint(worldObj.provider.dimensionId, xCoord, yCoord, zCoord, 32.0));
t.attackEntityFrom(DamageSource.magic, 4 + worldObj.rand.nextInt(4));
if (!all) break;
}
}
private void inEvHarm(final boolean all) {
final List<EntityLivingBase> targets = worldObj.getEntitiesWithinAABB(
EntityLivingBase.class,
AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord, xCoord + 1, yCoord + 1, zCoord + 1)
.expand(10, 10, 10));
if (targets == null || targets.isEmpty()) return;
for (final EntityLivingBase t : targets) {
if (worldObj.rand.nextBoolean()) {
t.addPotionEffect(new PotionEffect(Config.potionTaintPoisonID, 120, 0, false));
} else {
final PotionEffect pe = new PotionEffect(Config.potionVisExhaustID, 2400, 0, true);
pe.getCurativeItems().clear();
t.addPotionEffect(pe);
}
if (!all) break;
}
}
private void inEvWarp() {
final List<EntityPlayer> targets = worldObj.getEntitiesWithinAABB(
EntityPlayer.class,
AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord, xCoord + 1, yCoord + 1, zCoord + 1)
.expand(10, 10, 10));
if (targets == null || targets.isEmpty()) return;
final EntityPlayer t = targets.get(worldObj.rand.nextInt(targets.size()));
if (worldObj.rand.nextFloat() < 0.25f) {
Thaumcraft.addStickyWarpToPlayer(t, 1);
} else {
Thaumcraft.addWarpToPlayer(t, 1 + worldObj.rand.nextInt(5), true);
}
}
private void inEvEjectItem(final int type) {
for (int q = 0; q < 50 && !pedestals.isEmpty(); ++q) {
final ChunkCoordinates cc = pedestals.get(worldObj.rand.nextInt(pedestals.size()));
final TileEntity te = worldObj.getTileEntity(cc.posX, cc.posY, cc.posZ);
if (!(te instanceof TilePedestal)) continue;
final TilePedestal ped = (TilePedestal) te;
if (ped.getStackInSlot(0) == null) continue;
if (type < 3 || type == 5) InventoryUtils.dropItems(worldObj, cc.posX, cc.posY, cc.posZ);
else ped.setInventorySlotContents(0, null);
if (type == 1 || type == 3) {
worldObj.setBlock(cc.posX, cc.posY + 1, cc.posZ, ConfigBlocks.blockFluxGoo, 7, 3);
worldObj.playSoundEffect(cc.posX, cc.posY, cc.posZ, "game.neutral.swim", 0.3f, 1f);
} else if (type == 2 || type == 4) {
worldObj.setBlock(cc.posX, cc.posY + 1, cc.posZ, ConfigBlocks.blockFluxGas, 7, 3);
worldObj.playSoundEffect(cc.posX, cc.posY, cc.posZ, "random.fizz", 0.3f, 1f);
} else if (type == 5) {
worldObj.createExplosion(null, cc.posX + 0.5f, cc.posY + 0.5f, cc.posZ + 0.5f, 1f, false);
}
worldObj.addBlockEvent(cc.posX, cc.posY, cc.posZ, ConfigBlocks.blockStoneDevice, 11, 0);
thaumcraft.common.lib.network.PacketHandler.INSTANCE.sendToAllAround(
new PacketFXBlockZap(
xCoord + 0.5f,
yCoord + 0.5f,
zCoord + 0.5f,
cc.posX + 0.5f,
cc.posY + 1.5f,
cc.posZ + 0.5f),
new NetworkRegistry.TargetPoint(worldObj.provider.dimensionId, xCoord, yCoord, zCoord, 32.0));
return;
}
}
// -------------------------------------------------------------------------
// Craft cycle
// -------------------------------------------------------------------------
public void craftCycle() {
final TileVat vat = getVat();
final EntityLivingBase entity = (vat != null) ? vat.getEntityContained() : null;
// instability events
if (instability > 0 && worldObj.rand.nextInt(500) <= instability) {
switch (worldObj.rand.nextInt(21)) {
case 0, 2, 10, 13 -> inEvEjectItem(0);
case 6, 17 -> inEvEjectItem(1);
case 1, 11 -> inEvEjectItem(2);
case 3, 8, 14 -> inEvZap(false);
case 5, 16 -> inEvHarm(false);
case 12 -> inEvZap(true);
case 19 -> inEvEjectItem(3);
case 7 -> inEvEjectItem(4);
case 4, 15 -> inEvEjectItem(5);
case 18 -> inEvHarm(true);
case 9 -> worldObj.createExplosion(
null,
xCoord + 0.5f,
yCoord + 0.5f,
zCoord + 0.5f,
1.5f + worldObj.rand.nextFloat(),
false);
case 20 -> inEvWarp();
}
}
// instability damage to subject
if (instability > 0) {
float visDrawn = 999f;
if (!worldObj.isRemote) {
visDrawn = Math
.min(visDrawn, VisNetHandler.drainVis(worldObj, xCoord, yCoord, zCoord, Aspect.EARTH, 100));
visDrawn = Math
.min(visDrawn, VisNetHandler.drainVis(worldObj, xCoord, yCoord, zCoord, Aspect.WATER, 100));
visDrawn = Math
.min(visDrawn, VisNetHandler.drainVis(worldObj, xCoord, yCoord, zCoord, Aspect.ORDER, 100));
}
final float dmg = instability / 10f / (5f + visDrawn);
if (entity != null) {
entity.setHealth(entity.getHealth() - dmg);
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
if (entity.getHealth() <= 0f) {
killSubject();
return;
}
} else {
selfInfusionHealth -= dmg;
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
if (selfInfusionHealth <= 0f) {
killSubject();
return;
}
}
}
// draw essentia
if (recipeEssentia.visSize() > 0) {
for (final Aspect asp : recipeEssentia.getAspects()) {
if (recipeEssentia.getAmount(asp) > 0) {
if (tryDrawAllEssentia()) {
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
return;
}
if (worldObj.rand.nextInt(100 - recipeInstability * 3) == 0) {
++instability;
}
if (instability > 25) instability = 25;
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
break;
}
}
checkSurroundings = true;
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
return;
}
// consume ingredients from pedestals
if (recipeIngredients != null && !recipeIngredients.isEmpty()) {
for (int a = 0; a < recipeIngredients.size(); ++a) {
for (final ChunkCoordinates cc : pedestals) {
final TileEntity te = worldObj.getTileEntity(cc.posX, cc.posY, cc.posZ);
if (!(te instanceof TilePedestal)) continue;
final TilePedestal ped = (TilePedestal) te;
if (ped.getStackInSlot(0) == null) continue;
if (!InfusionRecipe.areItemStacksEqual(ped.getStackInSlot(0), recipeIngredients.get(a), true))
continue;
if (itemCount == 0) {
itemCount = 5;
PacketHandler.INSTANCE.sendToAllAround(
new PacketInfusionFX(
xCoord,
yCoord - 2,
zCoord,
(byte) (xCoord - cc.posX),
(byte) (yCoord - cc.posY - 2),
(byte) (zCoord - cc.posZ),
0),
new NetworkRegistry.TargetPoint(
worldObj.provider.dimensionId,
xCoord,
yCoord,
zCoord,
32.0));
} else if (itemCount-- <= 1) {
final ItemStack container = ped.getStackInSlot(0).getItem()
.getContainerItem(ped.getStackInSlot(0));
ped.setInventorySlotContents(0, container == null ? null : container.copy());
recipeIngredients.remove(a);
}
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
return;
}
}
return;
}
// all done
instability = 0;
craftingFinish(recipeOutput, recipeOutputLabel);
recipeOutput = null;
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
}
// -------------------------------------------------------------------------
// Kill subject (infusion failure)
// -------------------------------------------------------------------------
public void killSubject() {
final TileVat vat = getVat();
if (!worldObj.isRemote && vat != null) {
final EntityLivingBase entity = vat.getEntityContained();
if (entity != null || recipeType == 1) {
worldObj.createExplosion(null, xCoord + 0.5, yCoord - 0.5, zCoord + 0.5, 0.5f, false);
for (int a = 0; a < 25; ++a) {
final int xx = xCoord + worldObj.rand.nextInt(8) - worldObj.rand.nextInt(8);
final int yy = yCoord + worldObj.rand.nextInt(8) - worldObj.rand.nextInt(8);
final int zz = zCoord + worldObj.rand.nextInt(8) - worldObj.rand.nextInt(8);
if (worldObj.isAirBlock(xx, yy, zz)) {
worldObj.setBlock(
xx,
yy,
zz,
yy < yCoord ? ConfigBlocks.blockFluxGoo : ConfigBlocks.blockFluxGas,
8,
3);
}
}
}
}
onInfusionInterrupted();
if (vat != null) {
vat.selfInfusions = new int[12];
vat.setEntityContained(null);
vat.mode = 0;
vat.markDirty();
worldObj.markBlockForUpdate(vat.xCoord, vat.yCoord, vat.zCoord);
}
}
// -------------------------------------------------------------------------
// Infusion finish
// -------------------------------------------------------------------------
public void craftingFinish(final Object out, final String label) {
final TileVat vat = getVat();
if (vat == null) {
onInfusionInterrupted();
return;
}
if (recipeType == 0) {
if (out instanceof Integer) {
EntityLivingBase created = null;
if ((Integer) out < 0) {
created = (EntityLivingBase) net.minecraft.entity.EntityList
.createEntityByID(-(Integer) out, worldObj);
}
final ModContainer mc = Loader.instance().getIndexedModList().get("ThaumicHorizons");
try {
created = (EntityLivingBase) EntityRegistry.instance().lookupModSpawn(mc, (Integer) out)
.getEntityClass().getConstructor(World.class).newInstance(worldObj);
} catch (InvocationTargetException e) {
e.getCause().printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
created.copyLocationAndAnglesFrom(vat.getEntityContained());
created.copyDataFrom(vat.getEntityContained(), true);
if (created instanceof IEntityInfusedStats) {
((IEntityInfusedStats) created).resetStats();
}
vat.setEntityContained(created);
} else if (out instanceof NBTBase) {
final NBTTagCompound tagMods = (NBTTagCompound) out;
final Multimap<String, net.minecraft.entity.ai.attributes.AttributeModifier> map = HashMultimap
.create();
if (tagMods.getDouble("generic.movementSpeed") > 0.0) {
map.put(
"generic.movementSpeed",
new net.minecraft.entity.ai.attributes.AttributeModifier(
"generic.movementSpeed",
tagMods.getDouble("generic.movementSpeed") / 10.0,
1));
}
if (tagMods.getDouble("generic.maxHealth") > 0.0) {
map.put(
"generic.maxHealth",
new net.minecraft.entity.ai.attributes.AttributeModifier(
"generic.maxHealth",
tagMods.getDouble("generic.maxHealth"),
1));
}
if (tagMods.getDouble("generic.attackDamage") > 0.0) {
map.put(
"generic.attackDamage",
new net.minecraft.entity.ai.attributes.AttributeModifier(
"generic.attackDamage",
tagMods.getDouble("generic.attackDamage"),
1));
}
if (!map.isEmpty()) {
vat.getEntityContained().getAttributeMap().applyAttributeModifiers(map);
}
final Set<String> keys = (Set<String>) tagMods.func_150296_c();
for (final String s : keys) {
if (!s.startsWith("generic.")) {
final EntityInfusionProperties props = (EntityInfusionProperties) vat.getEntityContained()
.getExtendedProperties("CreatureInfusion");
props.addInfusion(tagMods.getInteger(s));
if (tagMods.getInteger(s) == 7) props.setOwner(recipePlayer);
}
}
}
((EntityInfusionProperties) vat.getEntityContained().getExtendedProperties("CreatureInfusion"))
.addCost(myEssentiaCost);
if (vat.getEntityContained() instanceof EntityLiving) {
((EntityLiving) vat.getEntityContained()).func_110163_bv();
}
vat.mode = 0;
} else {
for (int i = 0; i < vat.selfInfusions.length; ++i) {
if (vat.selfInfusions[i] == 0) {
vat.selfInfusions[i] = (Integer) recipeOutput;
break;
}
}
vat.mode = 4;
}
PacketHandler.INSTANCE.sendToAllAround(
new PacketFXInfusionDone(xCoord, yCoord - 1, zCoord),
new NetworkRegistry.TargetPoint(worldObj.provider.dimensionId, xCoord, yCoord, zCoord, 32.0));
onInfusionInterrupted();
vat.markDirty();
worldObj.markBlockForUpdate(vat.xCoord, vat.yCoord, vat.zCoord);
}
// -------------------------------------------------------------------------
// Interrupt / reset
// -------------------------------------------------------------------------
public void onInfusionInterrupted() {
crafting = false;
active = false;
recipeEssentia = new AspectList();
recipeIngredients = null;
recipeOutput = null;
recipePlayer = null;
recipeOutputLabel = "";
instability = 0;
selfInfusionHealth = 20f;
itemCount = 0;
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
}
// -------------------------------------------------------------------------
// Client-side effects
// -------------------------------------------------------------------------
protected void doEffects() {
if (!crafting) {
if (craftCount > 0) {
craftCount -= 2;
if (craftCount < 0) craftCount = 0;
}
return;
}
if (craftCount == 0) {
worldObj.playSound(xCoord, yCoord, zCoord, "thaumcraft:infuserstart", 0.5f, 1f, false);
} else if (craftCount % 65 == 0) {
worldObj.playSound(xCoord, yCoord, zCoord, "thaumcraft:infuser", 0.5f, 1f, false);
}
++craftCount;
Thaumcraft.proxy.blockRunes(
worldObj,
xCoord,
yCoord - 3,
zCoord,
0.5f + worldObj.rand.nextFloat() * 0.2f,
0.1f,
0.7f + worldObj.rand.nextFloat() * 0.3f,
25,
-0.03f);
if (instability > 0 && worldObj.rand.nextInt(200) <= instability) {
Thaumcraft.proxy.nodeBolt(
worldObj,
xCoord + 0.5f,
yCoord + 0.5f,
zCoord + 0.5f,
xCoord + 0.5f + (worldObj.rand.nextFloat() - worldObj.rand.nextFloat()) * 2f,
yCoord + 0.5f + (worldObj.rand.nextFloat() - worldObj.rand.nextFloat()) * 2f,
zCoord + 0.5f + (worldObj.rand.nextFloat() - worldObj.rand.nextFloat()) * 2f);
}
for (final String fxk : sourceFX.keySet().toArray(new String[0])) {
final SourceFX fx = sourceFX.get(fxk);
if (fx.ticks <= 0) {
sourceFX.remove(fxk);
} else {
if (fx.loc.posX == xCoord && fx.loc.posY == yCoord && fx.loc.posZ == zCoord) {
final Entity player = worldObj.getEntityByID(fx.color);
if (player != null) {
for (int a = 0; a < Thaumcraft.proxy.particleCount(2); ++a) {
Thaumcraft.proxy.drawInfusionParticles4(
worldObj,
player.posX
+ (worldObj.rand.nextFloat() - worldObj.rand.nextFloat()) * player.width,
player.boundingBox.minY + worldObj.rand.nextFloat() * player.height,
player.posZ
+ (worldObj.rand.nextFloat() - worldObj.rand.nextFloat()) * player.width,
xCoord,
yCoord,
zCoord);
}
}
} else {
final TileEntity tile = worldObj.getTileEntity(fx.loc.posX, fx.loc.posY, fx.loc.posZ);
if (tile instanceof TilePedestal) {
final ItemStack is = ((TilePedestal) tile).getStackInSlot(0);
if (is != null) {
if (worldObj.rand.nextInt(3) == 0) {
Thaumcraft.proxy.drawInfusionParticles3(
worldObj,
fx.loc.posX + worldObj.rand.nextFloat(),
fx.loc.posY + worldObj.rand.nextFloat() + 1f,
fx.loc.posZ + worldObj.rand.nextFloat(),
xCoord,
yCoord,
zCoord);
} else {
final Item bi = is.getItem();
final int md = is.getItemDamage();
if (is.getItemSpriteNumber() == 0 && bi instanceof ItemBlock) {
for (int a = 0; a < Thaumcraft.proxy.particleCount(2); ++a) {
Thaumcraft.proxy.drawInfusionParticles2(
worldObj,
fx.loc.posX + worldObj.rand.nextFloat(),
fx.loc.posY + worldObj.rand.nextFloat() + 1f,
fx.loc.posZ + worldObj.rand.nextFloat(),
xCoord,
yCoord,
zCoord,
Block.getBlockFromItem(bi),
md);
}
} else {
for (int a = 0; a < Thaumcraft.proxy.particleCount(2); ++a) {
Thaumcraft.proxy.drawInfusionParticles1(
worldObj,
fx.loc.posX + 0.4f + worldObj.rand.nextFloat() * 0.2f,
fx.loc.posY + 1.23f + worldObj.rand.nextFloat() * 0.2f,
fx.loc.posZ + 0.4f + worldObj.rand.nextFloat() * 0.2f,
xCoord,
yCoord,
zCoord,
bi,
md);
}
}
}
}
} else {
fx.ticks = 0;
}
}
--fx.ticks;
sourceFX.put(fxk, fx);
}
}
}
// -------------------------------------------------------------------------
// NBT
// -------------------------------------------------------------------------
@Override
public void readCustomNBT(final NBTTagCompound tag) {
super.readCustomNBT(tag);
crafting = tag.getBoolean("crafting");
active = tag.getBoolean("active");
instability = tag.getShort("instability");
selfInfusionHealth = tag.getFloat("selfInfusionHealth");
recipeEssentia = new AspectList();
final NBTTagList essList = tag.getTagList("recipeEssentia", 10);
for (int i = 0; i < essList.tagCount(); ++i) {
final NBTTagCompound e = essList.getCompoundTagAt(i);
if (e.hasKey("key")) recipeEssentia.add(Aspect.getAspect(e.getString("key")), e.getInteger("amount"));
}
recipeType = tag.getInteger("recipetype");
recipeInstability = tag.getInteger("recipeinst");
recipePlayer = tag.getString("recipeplayer");
if (recipePlayer.isEmpty()) recipePlayer = null;
}
@Override
public void writeCustomNBT(final NBTTagCompound tag) {
super.writeCustomNBT(tag);
tag.setBoolean("crafting", crafting);
tag.setBoolean("active", active);
tag.setShort("instability", (short) instability);
tag.setFloat("selfInfusionHealth", selfInfusionHealth);
final NBTTagList essList = new NBTTagList();
for (final Aspect asp : recipeEssentia.getAspects()) {
if (asp != null) {
final NBTTagCompound e = new NBTTagCompound();
e.setString("key", asp.getTag());
e.setInteger("amount", recipeEssentia.getAmount(asp));
essList.appendTag(e);
}
}
tag.setTag("recipeEssentia", essList);
tag.setInteger("recipetype", recipeType);
tag.setInteger("recipeinst", recipeInstability);
tag.setString("recipeplayer", recipePlayer == null ? "" : recipePlayer);
}
@Override
public void readFromNBT(final NBTTagCompound tag) {
super.readFromNBT(tag);
recipeIngredients = new ArrayList<>();
final NBTTagList ingredList = tag.getTagList("recipein", 10);
for (int i = 0; i < ingredList.tagCount(); ++i) {
final NBTTagCompound c = ingredList.getCompoundTagAt(i);
recipeIngredients.add(ItemStack.loadItemStackFromNBT(c));
}
final String rot = tag.getString("rotype");
if (rot.equals("@")) {
recipeOutput = tag.getInteger("recipeout");
} else if (!rot.isEmpty()) {
recipeOutputLabel = rot;
recipeOutput = tag.getTag("recipeout");
}
}
@Override
public void writeToNBT(final NBTTagCompound tag) {
super.writeToNBT(tag);
if (recipeIngredients != null && !recipeIngredients.isEmpty()) {
final NBTTagList ingredList = new NBTTagList();
int idx = 0;
for (final ItemStack stack : recipeIngredients) {
if (stack != null) {
final NBTTagCompound c = new NBTTagCompound();
c.setByte("item", (byte) idx++);
stack.writeToNBT(c);
ingredList.appendTag(c);
}
}
tag.setTag("recipein", ingredList);
}
if (recipeOutput instanceof Integer) {
tag.setString("rotype", "@");
tag.setTag("recipeout", new NBTTagInt((Integer) recipeOutput));
} else if (recipeOutput instanceof NBTBase) {
tag.setString("rotype", recipeOutputLabel);
tag.setTag("recipeout", (NBTBase) recipeOutput);
}
}
// -------------------------------------------------------------------------
// TileVisNode
// -------------------------------------------------------------------------
@Override
public int getRange() {
return 8;
@@ -104,4 +915,17 @@ public class TileVatMatrix extends TileVisNode implements IWandable, IAspectCont
public boolean isSource() {
return false;
}
public static class SourceFX {
public ChunkCoordinates loc;
public int ticks;
public int color;
public SourceFX(final ChunkCoordinates loc, final int ticks, final int color) {
this.loc = loc;
this.ticks = ticks;
this.color = color;
}
}
}
@@ -18,6 +18,8 @@ import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.DamageSource;
@@ -49,7 +51,7 @@ import thaumcraft.common.items.wands.ItemWandCasting;
public class TileVortex extends TileThaumcraft implements IWandable, IAspectContainer {
final int MAX_COUNT = 2400;
private static final int MAX_COUNT = 2400;
public int count;
public int beams;
public int dimensionID;
@@ -63,6 +65,9 @@ public class TileVortex extends TileThaumcraft implements IWandable, IAspectCont
public ArrayList<ItemStack> items;
Thread ppThread;
private boolean soundStarted = false;
// true once the first S35 description packet has been received on the client
public boolean clientSynced = false;
private int lastSyncedBeams = -1;
public TileVortex() {
this.aspects = new AspectList();
@@ -78,6 +83,13 @@ public class TileVortex extends TileThaumcraft implements IWandable, IAspectCont
public void updateEntity() {
super.updateEntity();
// Send one packet per tick when beams changes, so same-tick re/deHungrify
// pairs don't produce two packets with an intermediate wrong value.
if (!this.worldObj.isRemote && this.beams != this.lastSyncedBeams) {
this.worldObj.markBlockForUpdate(this.xCoord, this.yCoord, this.zCoord);
this.lastSyncedBeams = this.beams;
}
if (this.worldObj != null && this.worldObj.isRemote && !soundStarted) {
ThaumicHorizons.proxy.playVortexSound(this);
soundStarted = true;
@@ -149,7 +161,7 @@ public class TileVortex extends TileThaumcraft implements IWandable, IAspectCont
this.handlePocketPlaneStuff();
}
if (!this.cheat) {
this.count += 6 - this.beams;
this.count += Math.max(0, 6 - this.beams);
}
if (this.count > MAX_COUNT && !this.cheat
&& this.worldObj.provider.dimensionId != ThaumicHorizons.dimensionPocketId) {
@@ -175,7 +187,7 @@ public class TileVortex extends TileThaumcraft implements IWandable, IAspectCont
if (player.timeUntilPortal > 0) {
player.timeUntilPortal = 10;
} else if (player.dimension != ThaumicHorizons.dimensionPocketId) {
player.timeUntilPortal = 10;
player.timeUntilPortal = 200;
player.mcServer.getConfigurationManager().transferPlayerToDimension(
player,
ThaumicHorizons.dimensionPocketId,
@@ -183,7 +195,7 @@ public class TileVortex extends TileThaumcraft implements IWandable, IAspectCont
mServer.worldServerForDimension(ThaumicHorizons.dimensionPocketId),
this.dimensionID));
} else {
player.timeUntilPortal = 10;
player.timeUntilPortal = 200;
player.mcServer.getConfigurationManager().transferPlayerToDimension(
player,
this.returnID,
@@ -238,14 +250,15 @@ public class TileVortex extends TileThaumcraft implements IWandable, IAspectCont
} else
if (item.getEntityItem().getItem() == ThaumicHorizons.itemCrystalWand) {
final ItemStack theWand = new ItemStack(ThaumicHorizons.itemWandCastingDisposable);
((ItemWandCasting) theWand.getItem()).setRod(theWand, ThaumicHorizons.ROD_CRYSTAL);
((ItemWandCasting) theWand.getItem()).setCap(theWand, ThaumicHorizons.CAP_CRYSTAL);
((ItemWandCasting) theWand.getItem()).storeVis(theWand, Aspect.EARTH, 25000);
((ItemWandCasting) theWand.getItem()).storeVis(theWand, Aspect.AIR, 25000);
((ItemWandCasting) theWand.getItem()).storeVis(theWand, Aspect.FIRE, 25000);
((ItemWandCasting) theWand.getItem()).storeVis(theWand, Aspect.WATER, 25000);
((ItemWandCasting) theWand.getItem()).storeVis(theWand, Aspect.ORDER, 25000);
((ItemWandCasting) theWand.getItem()).storeVis(theWand, Aspect.ENTROPY, 25000);
final ItemWandCasting wand = (ItemWandCasting) theWand.getItem();
wand.setRod(theWand, ThaumicHorizons.ROD_CRYSTAL);
wand.setCap(theWand, ThaumicHorizons.CAP_CRYSTAL);
wand.storeVis(theWand, Aspect.EARTH, 25000);
wand.storeVis(theWand, Aspect.AIR, 25000);
wand.storeVis(theWand, Aspect.FIRE, 25000);
wand.storeVis(theWand, Aspect.WATER, 25000);
wand.storeVis(theWand, Aspect.ORDER, 25000);
wand.storeVis(theWand, Aspect.ENTROPY, 25000);
this.items.add(theWand);
item.setDead();
} else
@@ -321,7 +334,7 @@ public class TileVortex extends TileThaumcraft implements IWandable, IAspectCont
this.xCoord,
this.yCoord,
this.zCoord,
this.returnID))).run();
this.returnID))).start();
}
this.markDirty();
}
@@ -375,24 +388,25 @@ public class TileVortex extends TileThaumcraft implements IWandable, IAspectCont
}
}
}
final double var3 = (this.xCoord + 0.5 - eo.posX) / 15.0;
final double var4 = (this.yCoord + 0.5 - eo.posY) / 15.0;
final double var5 = (this.zCoord + 0.5 - eo.posZ) / 15.0;
final double var6 = Math.sqrt(var3 * var3 + var4 * var4 + var5 * var5);
double var7 = 1.0 - var6;
final double dx = (this.xCoord + 0.5 - eo.posX) / 15.0;
final double dy = (this.yCoord + 0.5 - eo.posY) / 15.0;
final double dz = (this.zCoord + 0.5 - eo.posZ) / 15.0;
final double dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
double var7 = 1.0 - dist;
final double modifier = 2.0 - this.beams / 3.0;
if (var7 <= 0.0) {
continue;
}
var7 *= var7;
eo.motionX += var3 / var6 * var7 * 0.15 * modifier;
eo.motionY += var4 / var6 * var7 * 0.25 * modifier;
eo.motionZ += var5 / var6 * var7 * 0.15 * modifier;
eo.motionX += dx / dist * var7 * 0.15 * modifier;
eo.motionY += dy / dist * var7 * 0.25 * modifier;
eo.motionZ += dz / dist * var7 * 0.15 * modifier;
}
}
}
for (int i = 0; i < 3; ++i) {
if (!this.worldObj.isRemote && (this.beams <= 0 || this.worldObj.rand.nextInt(this.beams) == 0)) {
if (!this.worldObj.isRemote
&& (this.beams <= 0 || this.worldObj.rand.nextInt(Math.max(1, this.beams)) == 0)) {
int tx2 = this.xCoord + this.worldObj.rand.nextInt(16) - this.worldObj.rand.nextInt(16);
int ty2 = this.yCoord + this.worldObj.rand.nextInt(16) - this.worldObj.rand.nextInt(16);
int tz2 = this.zCoord + this.worldObj.rand.nextInt(16) - this.worldObj.rand.nextInt(16);
@@ -579,6 +593,19 @@ public class TileVortex extends TileThaumcraft implements IWandable, IAspectCont
return 0;
}
@Override
public net.minecraft.network.Packet getDescriptionPacket() {
NBTTagCompound nbt = new NBTTagCompound();
this.writeCustomNBT(nbt);
return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 1, nbt);
}
@Override
public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
this.readCustomNBT(pkt.func_148857_g());
this.clientSynced = true;
}
@Override
public void invalidate() {
super.invalidate();
@@ -39,7 +39,7 @@ public class TileVortexStabilizer extends TileThaumcraft implements IWandable {
public boolean redstoned;
public ForgeDirection dir;
public Object theBeam;
public Entity[] sonicFX;
private Entity[] sonicFX;
public TileVortexStabilizer() {
this.xTarget = Integer.MAX_VALUE;
@@ -86,20 +86,20 @@ public class TileVortexStabilizer extends TileThaumcraft implements IWandable {
if (this.hasTarget) {
this.reHungrifyTarget();
this.hasTarget = false;
} else if (!this.hasTarget
&& this.worldObj.getTileEntity(mop.blockX, mop.blockY, mop.blockZ) instanceof INode) {
this.hasTarget = true;
this.target = this.worldObj.getTileEntity(mop.blockX, mop.blockY, mop.blockZ);
this.prevType = ((INode) this.worldObj
.getTileEntity(mop.blockX, mop.blockY, mop.blockZ)).getNodeType().ordinal();
this.deHungrifyTarget();
} else
if (!this.hasTarget && this.worldObj
.getTileEntity(mop.blockX, mop.blockY, mop.blockZ) instanceof TileVortex) {
this.hasTarget = true;
this.target = this.worldObj.getTileEntity(mop.blockX, mop.blockY, mop.blockZ);
this.deHungrifyTarget();
}
}
// Separate if (not else if) so a new valid target is acquired in the same tick,
// preventing a window where beams is decremented but not yet restored.
final TileEntity newTE = this.worldObj.getTileEntity(mop.blockX, mop.blockY, mop.blockZ);
if (newTE instanceof INode) {
this.hasTarget = true;
this.target = newTE;
this.prevType = ((INode) newTE).getNodeType().ordinal();
this.deHungrifyTarget();
} else if (newTE instanceof TileVortex) {
this.hasTarget = true;
this.target = newTE;
this.deHungrifyTarget();
}
this.xTarget = mop.blockX;
this.yTarget = mop.blockY;
this.zTarget = mop.blockZ;
@@ -166,25 +166,46 @@ public class TileVortexStabilizer extends TileThaumcraft implements IWandable {
public void reHungrifyTarget() {
if (this.target instanceof INode) {
((INode) this.target).setNodeType(NodeType.values()[this.prevType]);
} else if (this.target instanceof final TileVortex tileVortex) {
--tileVortex.beams;
if (!isNodeTargetedByOtherStabilizer()) {
((INode) this.target).setNodeType(NodeType.values()[this.prevType]);
}
} else if (this.target instanceof TileVortex) {
--((TileVortex) this.target).beams;
}
if (this.target != null) {
this.target.markDirty();
this.worldObj.markBlockForUpdate(this.target.xCoord, this.target.yCoord, this.target.zCoord);
}
}
private boolean isNodeTargetedByOtherStabilizer() {
// A stabilizer can only reach a node from up to 10 blocks away in one of 6 directions,
// so check those 60 positions instead of scanning all loaded tile entities.
int tx = this.target.xCoord;
int ty = this.target.yCoord;
int tz = this.target.zCoord;
for (ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) {
for (int dist = 1; dist <= 10; dist++) {
TileEntity te = this.worldObj
.getTileEntity(tx - d.offsetX * dist, ty - d.offsetY * dist, tz - d.offsetZ * dist);
if (te instanceof TileVortexStabilizer && te != this) {
TileVortexStabilizer other = (TileVortexStabilizer) te;
if (other.hasTarget && other.target == this.target) {
return true;
}
}
}
}
return false;
}
void deHungrifyTarget() {
if (this.target instanceof INode) {
((INode) this.target).setNodeType(NodeType.NORMAL);
} else if (this.target instanceof final TileVortex tileVortex) {
++tileVortex.beams;
} else if (this.target instanceof TileVortex) {
++((TileVortex) this.target).beams;
}
if (this.target != null) {
this.target.markDirty();
this.worldObj.markBlockForUpdate(this.target.xCoord, this.target.yCoord, this.target.zCoord);
}
}
@@ -296,7 +296,7 @@ healingVatContainment= §nContainment Focus Unlocked§r<IMG>thaumichorizons:
tc.research_name.infusionVat=Creature Infusion
tc.research_text.infusionVat=Harder, faster, stronger
infusionVat1=To infuse a living creature would be an unprecedented feat: thaumaturgic texts are filled with cautionary tales of grotesque deformities as the creature's body rejects the foreign magic. Your curative vats may finally allow this procedure to succeed by suspending the organism's vital functions while the infusion is applied.<BR>You must first install a modified runic matrix atop the vat. This is more difficult than it sounds, as attempting to infuse a runic matrix produces considerable magical feedback between the two matrices. The prudent thaumaturge would ensure that her altar is well-prepared.
infusionVat2=Once the infusion vat has been set up, its operation is almost identical to the standard infusion altar, although essentia must be piped in directly.<BR>The process is somewhat riskier than infusing inanimate matter, however. In addition to the usual effects, accumulated instability will damage the subject. Worse, the process cannot be aborted; it will end in either success or death.<BR>Less hardy creatures may not survive complex infusions without outside help. Your experiments have shown that providing a combination of §3Aqua§0, §7Ordo§0, and §2Terra§0 centivis can slow the rate of injury.
infusionVat2=Once the infusion vat has been set up, its operation is almost identical to that of the standard infusion altar, although essentia must be piped in directly.<BR>The process is somewhat riskier than infusing inanimate matter, however. In addition to the usual effects, accumulated instability will damage the subject. Worse, the process cannot be aborted; it will end in either success or death.<BR>Less hardy creatures may not survive complex infusions without outside help. Your experiments have shown that providing a combination of §3Aqua§0, §7Ordo§0, and §2Terra§0 centivis can slow the rate of injury.
infusionVat3=The infusion vat has a few limitations. Chief among them is the fact that humans (and other sentient beings) cannot be infused, as the human soul actively interferes with the addition of magical energy. Likewise, creatures of an already magical nature such as undead and golems will not be affected.<BR>Also, while a given creature can have any number of general infusions, it may possess only one species-specific infusion.<BR>Unfortunately, infused creatures do not pass on their infusions to offspring.
infusionVatClone= §nIncarnation Unlocked§r<BR>Your incarnation procedure can clone infused creatures. This requires additional essentia; you will need to supply the sum of every aspect from each infusion that the creature was subjected to.<BR>Curiously, the blood sample provides such a complete pattern for the developing organism that no instability is generated and no physical objects are required, making cloning a very efficient way of propagating your creations.
tc.research_name.generalInfusion=General Infusions
@@ -483,15 +483,15 @@ tc.research_text.chameleonSkin=Now you see me...
chameleonSkin1=Whether sneaking into a rival's laboratory or taking notes on the life cycle of the eldritch crab, there are times when you'd like to remain unobserved. Perhaps you can come up with a more permanent solution than chugging invisibility potions?<BR>The §5Chameleon Skin§0 infusion implants magical pigments and microscopic eyes into your skin, reflexively altering its color to match objects on the opposite side. The effect is much like constant invisibility, including its inability to hide armor.<BR>This effect can be toggled using the X key.
tc.research_name.tumorOops=Eldritch Apotheosis
tc.research_text.tumorOops=All is the Pattern
tumorOops1=You believed you had mastered your world and everything upon it. You fool.<BR>It is finally clear. The Eldritch are not beings in the ordinary sense, not crude things of meat and blood and bone like yourself. They are a pattern written in the stars, in the currents and eddies of magic, encoded deep in the structure of life itself.<BR>But the Pattern is fragmented, sundered and spread throughout the cosmos by some primordial catacylsm which can only be guessed at. It has sought to reassemble itself since the dawn of time, and now you have played your role in its life cycle.
tumorOops2=All of your studies, your creations, your journeys have served the Pattern. Learning the laws of magic invited it into your mind. Crafting forbidden artifacts allowed it to replicate itself into physical matter. And once you survived the trials of the Outer Lands, the Pattern judged you to have accumulated enough of itself.<BR>Enough to craft into a greater being. The next stage of its life cycle.<BR>But you still live. Perhaps it is not too late. Perhaps you can understand more of the Pattern- perhaps you can still stop it-<BR>Or incubate more. So many more.
tumorOops1=You believed you had mastered your world and everything upon it. You fool.<BR>It is finally clear. The Eldritch are not beings in the ordinary sense, not crude things of meat and blood and bone like yourself. They are a pattern written in the stars, in the currents and eddies of magic, encoded deep in the structure of life itself.<BR>But the Pattern is fragmented, sundered and spread throughout the cosmos by some primordial cataclysm which can only be guessed at. It has sought to reassemble itself since the dawn of time, and now you have played your role in its life cycle.
tumorOops2=All of your studies, your creations, your journeys have served the Pattern. Learning the laws of magic invited it into your mind. Crafting forbidden artifacts allowed it to replicate itself into physical matter. And once you survived the trials of the Outer Lands, the Pattern judged you to have accumulated enough of itself.<BR>Enough to craft into a greater being. The next stage of its life cycle.<BR>But you still live. Perhaps it is not too late. Perhaps you can understand more of the Pattern - perhaps you can still stop it-<BR>Or incubate more. So many more.
tc.research_name.construction=Arcane Construction
tc.research_text.construction=Don't tell the golems' union
construction1=TODO
tc.research_name.planarTheory=Planar Theory
tc.research_text.planarTheory=Deeper and deeper
planarTheory1=The cosmos is vaster and more varied than you could have imagined, but certain patterns are starting to emerge. Most importantly, you finally understand what lies between the planes.<BR>It is not a void in the traditional sense, for even the emptiest places are subject to the laws of space and time. Rather, the interplanar substance could be described as frozen possibility - infinite potential with nothing to act upon. Only when it leaks through an aura node in the form of vis, into a world with established natural law, can this potential be actualized.<BR>Such is the root of all magic.
planarTheory2=Of course, understanding this from an academic perspective is very different from being able to put it into practice. So far, all of your attempts to develop synthetic aura nodes have met with failure. Something is still missing...<BR>One offshoot of this research may warrant further study, however. By shattering an ender pearl, filling the resulting tesseract with ethereal essence, and encasing the structure in voidmetal you have created a remarkably stable device capable of manipulating rips in spacetime.<BR>Perhaps you will find some use for it.
planarTheory2=Of course, understanding this from an academic perspective is very different from being able to put it into practice. So far, all of your attempts to develop synthetic aura nodes have met with failure. Something is still missing...<BR>One offshoot of this research may warrant further study, however. By shattering an ender pearl, filling the resulting tesseract with ethereal essence, and encasing the structure in voidmetal, you have created a remarkably stable device capable of manipulating rips in spacetime.<BR>Perhaps you will find some use for it.
tc.research_name.transductionAmplifier=Transduction Amplifier
tc.research_text.transductionAmplifier=Full power!
transductionAmplifier1=Without outside interference, an aura node accumulates vis in short spurts as its dimensional breach fluctuates in size. Your node transducers can hold a node open at its largest natural diameter, but actually expanding the breach was not possible until recent advances in planar theory.<BR>You have developed a device which creates spatial turbulence, allowing microscopic rips to merge with an existing energized node. Attaching a transduction amplifier to one's side and applying a redstone signal can increase its centivis output as though the original node had ten additional vis per aspect.
@@ -515,7 +515,7 @@ planarRift2=This is unfortunate - an extremely high-vis node might have containe
planarRift3=And what then? Passing matter into the void would immerse it in a sea of endless vis, without grinding it down to its base aspects like an ordinary hungry node. Who can imagine what staggering works of thaumaturgy could be accomplished with this resource at hand?<BR>You imagine there will be severe limitations - most materials will still not survive immersion - but you already understand the basic nature of the process. By right-clicking a rift with your wand, you can tug on ethereal currents and retrieve any surviving objects.<BR>It is now time to determine what can be crafted in this manner...
tc.research_name.wispSpawn=Wisp Propagation
tc.research_text.wispSpawn=Kicking the hornet's nest
wispSpawn1=Casting a pile of salis mundus into a stabilized rift has a curious result: the salt grains absorb enough vis to resonate with one another and clump together into a quasi-living being.<BR>These artificially generated wisps are almost identical to the ones which naturally grow in magical environs. Note that the wisps' aspects will depend on which aspects were present in the parent node.<BR>The number of wisps generated is highly variable, and an incautious thamaturge can find himself quickly overwhelmed.
wispSpawn1=Casting a pile of salis mundus into a stabilized rift has a curious result: the salt grains absorb enough vis to resonate with one another and clump together into a quasi-living being.<BR>These artificially generated wisps are almost identical to the ones which naturally grow in magical environs. Note that the wisps' aspects will depend on which aspects were present in the parent node.<BR>The number of wisps generated is highly variable, and an incautious thaumaturge can find himself quickly overwhelmed.
tc.research_name.crystalWand=Crystalline Wand
tc.research_text.crystalWand=Disposable magic
crystalWand1=All advanced wands incorporate shards into their design, but only for the purpose of amplifying another material's ability to store vis. On their own, shards are wholly incapable of absorbing additional magic and so cannot drain an aura node - but what if you were to take them straight to the source?<BR>By constructing a wandlike arrangement of crystals and passing it into the void, you have created a curious device - a wand with tremendous vis capacity but no ability to ever recharge itself. Perhaps it could be used to craft objects unattainable with traditional wands.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 788 B

After

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 B

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 B

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 B

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 B

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 849 B

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 745 B

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 B

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 726 B

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 971 B

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 B

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 579 B

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 781 B

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 681 B

After

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 937 B

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 607 B

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 B

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 682 B

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 990 B

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 721 B

Loaded 100 of 302 files, more files were not shown because too many files have changed in this diff. Show more