Compare commits

..

3 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 91e75ee7f1 Clarify that singleUseSpellBooks config does not affect scrolls
Co-authored-by: WinDanesz <31292708+WinDanesz@users.noreply.github.com>
2025-11-02 00:10:00 +00:00
copilot-swe-agent[bot] 1d80c5a447 Initial analysis of spell book consumption feature
Co-authored-by: WinDanesz <31292708+WinDanesz@users.noreply.github.com>
2025-11-02 00:08:10 +00:00
copilot-swe-agent[bot] 9906b3e154 Initial plan 2025-11-02 00:04:59 +00:00
42 changed files with 172 additions and 532 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"homepage": "https://www.curseforge.com/minecraft/mc-mods/electroblobs-wizardry", "homepage": "https://www.curseforge.com/minecraft/mc-mods/electroblobs-wizardry",
"promos": { "promos": {
"1.12.2-latest": "4.3.17", "1.12.2-latest": "4.3.15",
"1.12.2-recommended": "4.3.17" "1.12.2-recommended": "4.3.15"
} }
} }
-50
View File
@@ -1,50 +0,0 @@
name: Gradle Build
on:
push:
branches: [ "**" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up JDK 8
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'temurin'
- name: Load Cache
uses: actions/cache@v5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
cache-read-only: ${{ github.ref != 'refs/heads/1.12.2' }}
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build --max-workers=1 --no-daemon
- name: Upload build artifacts
if: success()
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: build/libs/*.jar
retention-days: 30
+1 -1
View File
@@ -38,7 +38,7 @@ apply plugin: org.ajoberstar.grgit.gradle.GrgitPlugin
apply plugin: 'wtf.gofancy.fancygradle' apply plugin: 'wtf.gofancy.fancygradle'
version = "4.3.17" version = "4.3.15"
group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "ElectroblobsWizardry" archivesBaseName = "ElectroblobsWizardry"
Vendored Regular → Executable
View File
@@ -43,10 +43,6 @@ public class CommonProxy {
public void registerRenderers(){} public void registerRenderers(){}
public void registerItemColorHandlers(){}
public void registerModelProperties(){}
public void initialiseLayers(){} public void initialiseLayers(){}
public void initialiseAnimations(){} public void initialiseAnimations(){}
@@ -179,8 +175,6 @@ public class CommonProxy {
public void handleConquerShrinePacket(PacketConquerShrine.Message message){} public void handleConquerShrinePacket(PacketConquerShrine.Message message){}
public void handleBombExplosionPacket(PacketBombExplosion.Message message){}
// SECTION Misc // SECTION Misc
// =============================================================================================================== // ===============================================================================================================
@@ -207,7 +207,7 @@ public final class Settings {
/** <b>[Server-only]</b> Whether damage should be registered with the old system (wizardry_magic/indirect_wizardry_magic) prefixed damage with the elements like /** <b>[Server-only]</b> Whether damage should be registered with the old system (wizardry_magic/indirect_wizardry_magic) prefixed damage with the elements like
* necromancy_indirect_wizardry_magic, necromancy_wizardry_magic*/ * necromancy_indirect_wizardry_magic, necromancy_wizardry_magic*/
public boolean damageTypePerElement = false; public boolean damageTypePerElement = false;
/** <b>[Server-only]</b> Whether spell books are consumed when they are bound to a wand.*/ /** <b>[Server-only]</b> Whether spell books are consumed when they are bound to a wand. Does not affect scrolls.*/
public boolean singleUseSpellBooks = false; public boolean singleUseSpellBooks = false;
/** <b>[Server-only]</b> Whether to prevent binding the same spell to a wand multiple times*/ /** <b>[Server-only]</b> Whether to prevent binding the same spell to a wand multiple times*/
public boolean preventBindingSameSpellTwiceToWands = false; public boolean preventBindingSameSpellTwiceToWands = false;
@@ -355,8 +355,6 @@ public final class Settings {
* velocity-based one. * velocity-based one.
*/ */
public boolean replaceVanillaFallDamage = true; public boolean replaceVanillaFallDamage = true;
/** <b>[Synchronised]</b> Whether spell book colors are only shown when the player has the Archivist's Eyeglass equipped. */
public boolean spellBookColorsRequireArchivistsEyeglass = false;
/** <b>[Synchronised]</b> Chance of 'misreading' an undiscovered spell and triggering a forfeit instead. */ /** <b>[Synchronised]</b> Chance of 'misreading' an undiscovered spell and triggering a forfeit instead. */
public double forfeitChance = 0.2; public double forfeitChance = 0.2;
/** <b>[Synchronised]</b> Progression requirements for upgrading a wand to each tier. */ /** <b>[Synchronised]</b> Progression requirements for upgrading a wand to each tier. */
@@ -463,8 +461,6 @@ public final class Settings {
public static final String DEFAULT_HUD_SKIN_KEY = "default"; // Defined here so it's not in a client-only class. public static final String DEFAULT_HUD_SKIN_KEY = "default"; // Defined here so it's not in a client-only class.
/** <b>[Client-only]</b> The string identifier of the skin used for the spell HUD. */ /** <b>[Client-only]</b> The string identifier of the skin used for the spell HUD. */
public String spellHUDSkin = DEFAULT_HUD_SKIN_KEY; public String spellHUDSkin = DEFAULT_HUD_SKIN_KEY;
/** <b>[Client-only]</b> Whether to show elemental colors on spell books for discovered spells. */
public boolean spellBookColors = true;
/** Set of constants for each of the eight positions that the spell HUD can be in. */ /** Set of constants for each of the eight positions that the spell HUD can be in. */
public enum GuiPosition { public enum GuiPosition {
@@ -727,7 +723,7 @@ public final class Settings {
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "singleUseSpellBooks", false, property = config.get(GAMEPLAY_CATEGORY, "singleUseSpellBooks", false,
"Whether spell books are consumed when they are bound to a wand."); "Whether spell books are consumed when they are bound to a wand. This does not affect scrolls, which cannot be placed in the arcane workbench.");
property.setLanguageKey("config." + Wizardry.MODID + ".single_use_spell_books"); property.setLanguageKey("config." + Wizardry.MODID + ".single_use_spell_books");
Wizardry.proxy.setToNamedBooleanEntry(property); Wizardry.proxy.setToNamedBooleanEntry(property);
singleUseSpellBooks = property.getBoolean(); singleUseSpellBooks = property.getBoolean();
@@ -1107,13 +1103,6 @@ public final class Settings {
replaceVanillaFallDamage = property.getBoolean(); replaceVanillaFallDamage = property.getBoolean();
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "spellBookColorsRequireArchivistsEyeglass", false,
"If true, spell book colors are only shown when the player has the charm of spell discovery equipped.");
property.setLanguageKey("config." + Wizardry.MODID + ".spell_book_colors_require_charm");
Wizardry.proxy.setToNamedBooleanEntry(property);
spellBookColorsRequireArchivistsEyeglass = property.getBoolean();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "blindnessTweak", true, property = config.get(TWEAKS_CATEGORY, "blindnessTweak", true,
"Whether to tweak the blindness effect to reduce follow distance when used on non-players. This automatically disables itself in favour of Potion Core's implementation if installed."); "Whether to tweak the blindness effect to reduce follow distance when used on non-players. This automatically disables itself in favour of Potion Core's implementation if installed.");
property.setLanguageKey("config." + Wizardry.MODID + ".blindness_tweak"); property.setLanguageKey("config." + Wizardry.MODID + ".blindness_tweak");
@@ -1224,7 +1213,7 @@ public final class Settings {
bookshelfSearchRadius = property.getInt(); bookshelfSearchRadius = property.getInt();
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade. To specify metadata, use the format 'modid:item:meta value'. For example, 'minecraft:wool:1 5'. If no metadata is given, any metadata will be accepted.", property = config.get(TWEAKS_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade.",
Pattern.compile("[A-Za-z0-9:_]+ [0-9]+")); Pattern.compile("[A-Za-z0-9:_]+ [0-9]+"));
property.setLanguageKey("config." + Wizardry.MODID + ".currency_items"); property.setLanguageKey("config." + Wizardry.MODID + ".currency_items");
propOrder.add(property.getName()); propOrder.add(property.getName());
@@ -1498,14 +1487,6 @@ public final class Settings {
Wizardry.proxy.setToNamedBooleanEntry(property); Wizardry.proxy.setToNamedBooleanEntry(property);
showChargeMeter = property.getBoolean(); showChargeMeter = property.getBoolean();
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "spellBookColors", true, "Whether to show elemental colors on spell books for discovered spells.");
property.setLanguageKey("config." + Wizardry.MODID + ".spell_book_colors");
property.setRequiresWorldRestart(false);
Wizardry.proxy.setToNamedBooleanEntry(property);
spellBookColors = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "loadHandbook", true, "Whether to initialise the in-game handbook. Setting this to false will brick the in-game handbook, but it might help if you have startup crashes."); property = config.get(CLIENT_CATEGORY, "loadHandbook", true, "Whether to initialise the in-game handbook. Setting this to false will brick the in-game handbook, but it might help if you have startup crashes.");
property.setLanguageKey("config." + Wizardry.MODID + ".load_handbook"); property.setLanguageKey("config." + Wizardry.MODID + ".load_handbook");
property.setRequiresWorldRestart(false); property.setRequiresWorldRestart(false);
@@ -1836,46 +1817,16 @@ public final class Settings {
string = string.toLowerCase(Locale.ROOT).trim(); string = string.toLowerCase(Locale.ROOT).trim();
String[] itemArgs = string.split(":");
String item; String item;
short meta; short meta;
int lastColon = string.lastIndexOf(':'); try {
meta = Short.parseShort(itemArgs[itemArgs.length-1]);
if (lastColon > -1 && lastColon < string.length() - 1) { // if there is a colon and it's not the last char item = String.join(":", Arrays.copyOfRange(itemArgs, 0, itemArgs.length-1));
String metaString = string.substring(lastColon + 1); }catch(NumberFormatException e){ // If no metadata is specified
try {
short parsedMeta = Short.parseShort(metaString);
String itemString = string.substring(0, lastColon);
// If itemString has no colon, it could be 'item:meta' or 'namespace:numeric_path'.
// e.g., 'wool:1' vs 'mod:123'. We want to support 'wool:1' as item 'wool' with meta 1.
// We can't reliably distinguish the two cases without knowing all namespaces, so we have to use
// a heuristic. The original code favoured the 'item:meta' interpretation, let's stick with that.
// The issue was with 'namespace:numeric_path' which this logic now handles.
if (itemString.indexOf(':') == -1) {
item = itemString;
meta = parsedMeta;
} else {
// Disambiguate things like 'mod:123:4' (item 'mod:123' with meta 4) vs 'mod:item:123' (item 'mod:item' with meta 123)
// If the bit before the last colon is a valid resource location, it's probably item:meta
try {
new ResourceLocation(itemString);
item = itemString;
meta = parsedMeta;
} catch (Exception e) {
item = string;
meta = OreDictionary.WILDCARD_VALUE;
}
}
} catch (NumberFormatException e) {
// The part after colon is not a number, so it's part of the name
item = string;
meta = OreDictionary.WILDCARD_VALUE;
}
} else {
item = string;
meta = OreDictionary.WILDCARD_VALUE; meta = OreDictionary.WILDCARD_VALUE;
item = string;
} }
return Pair.of(new ResourceLocation(item), meta); return Pair.of(new ResourceLocation(item), meta);
@@ -82,7 +82,7 @@ public class Wizardry {
* 1.x.x represents Minecraft 1.7.x versions, 2.x.x represents Minecraft 1.10.x versions, 3.x.x represents Minecraft * 1.x.x represents Minecraft 1.7.x versions, 2.x.x represents Minecraft 1.10.x versions, 3.x.x represents Minecraft
* 1.11.x versions, and so on. * 1.11.x versions, and so on.
*/ */
public static final String VERSION = "4.3.17"; public static final String VERSION = "4.3.15";
/** /**
* Json file used by Forge's built-in <a href="https://mcforge.readthedocs.io/en/1.12.x/gettingstarted/autoupdate/">update checker</a>. * Json file used by Forge's built-in <a href="https://mcforge.readthedocs.io/en/1.12.x/gettingstarted/autoupdate/">update checker</a>.
@@ -198,8 +198,6 @@ public class Wizardry {
// Client-side stuff (via proxies) // Client-side stuff (via proxies)
proxy.initGuiBits(); proxy.initGuiBits();
proxy.registerParticles(); proxy.registerParticles();
proxy.registerItemColorHandlers();
proxy.registerModelProperties();
proxy.registerSoundEventListener(); proxy.registerSoundEventListener();
} }
@@ -1,6 +1,7 @@
package electroblob.wizardry.client; package electroblob.wizardry.client;
import electroblob.wizardry.CommonProxy; import electroblob.wizardry.CommonProxy;
import electroblob.wizardry.Settings;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockBookshelf; import electroblob.wizardry.block.BlockBookshelf;
import electroblob.wizardry.client.animation.ActionAnimation; import electroblob.wizardry.client.animation.ActionAnimation;
@@ -18,8 +19,6 @@ import electroblob.wizardry.client.model.ModelRobeArmour;
import electroblob.wizardry.client.model.ModelSageArmour; import electroblob.wizardry.client.model.ModelSageArmour;
import electroblob.wizardry.client.model.ModelWizardArmour; import electroblob.wizardry.client.model.ModelWizardArmour;
import electroblob.wizardry.client.particle.*; import electroblob.wizardry.client.particle.*;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.client.particle.ParticleWizardry.IWizardryParticleFactory; import electroblob.wizardry.client.particle.ParticleWizardry.IWizardryParticleFactory;
import electroblob.wizardry.client.renderer.RenderSpectralGolem; import electroblob.wizardry.client.renderer.RenderSpectralGolem;
import electroblob.wizardry.client.renderer.entity.*; import electroblob.wizardry.client.renderer.entity.*;
@@ -39,7 +38,6 @@ import electroblob.wizardry.entity.projectile.*;
import electroblob.wizardry.event.SpellCastEvent; import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.event.SpellCastEvent.Source; import electroblob.wizardry.event.SpellCastEvent.Source;
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration; import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.item.ItemScroll; import electroblob.wizardry.item.ItemScroll;
import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.item.ItemWand;
@@ -717,65 +715,6 @@ public class ClientProxy extends CommonProxy {
}else Wizardry.logger.warn("Received a PacketConquerShrine, but there was no shrine core at the position sent"); }else Wizardry.logger.warn("Received a PacketConquerShrine, but there was no shrine core at the position sent");
} }
@Override
public void handleBombExplosionPacket(PacketBombExplosion.Message message){
net.minecraft.world.World world = Minecraft.getMinecraft().world;
double x = message.x, y = message.y, z = message.z;
float blastMultiplier = message.blastMultiplier;
switch(message.bombType){
case PacketBombExplosion.FIREBOMB:
ParticleBuilder.create(Type.FLASH).pos(x, y, z).scale(5 * blastMultiplier).clr(1, 0.6f, 0).spawn(world);
for(int i = 0; i < 60 * blastMultiplier; i++){
ParticleBuilder.create(Type.MAGIC_FIRE, world.rand, x, y, z, 2 * blastMultiplier, false)
.time(10 + world.rand.nextInt(4)).scale(2 + world.rand.nextFloat()).spawn(world);
ParticleBuilder.create(Type.DARK_MAGIC, world.rand, x, y, z, 2 * blastMultiplier, false)
.clr(1.0f, 0.2f + world.rand.nextFloat() * 0.4f, 0.0f).spawn(world);
}
world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, x, y, z, 0, 0, 0);
break;
case PacketBombExplosion.POISON_BOMB:
ParticleBuilder.create(Type.FLASH).pos(x, y, z).scale(5 * blastMultiplier)
.clr(0.2f + world.rand.nextFloat() * 0.3f, 0.6f, 0.0f).spawn(world);
for(int i = 0; i < 60 * blastMultiplier; i++){
ParticleBuilder.create(Type.SPARKLE, world.rand, x, y, z, 2 * blastMultiplier, false).time(35)
.scale(2).clr(0.2f + world.rand.nextFloat() * 0.3f, 0.6f, 0.0f).spawn(world);
ParticleBuilder.create(Type.DARK_MAGIC, world.rand, x, y, z, 2 * blastMultiplier, false)
.clr(0.2f + world.rand.nextFloat() * 0.2f, 0.8f, 0.0f).spawn(world);
}
world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, x, y, z, 0, 0, 0);
break;
case PacketBombExplosion.SMOKE_BOMB:
ParticleBuilder.create(Type.FLASH).pos(x, y, z).scale(5 * blastMultiplier).clr(0, 0, 0).spawn(world);
world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, x, y, z, 0, 0, 0);
for(int i = 0; i < 60 * blastMultiplier; i++){
float brightness = world.rand.nextFloat() * 0.1f + 0.1f;
ParticleBuilder.create(Type.CLOUD, world.rand, x, y, z, 2 * blastMultiplier, false)
.clr(brightness, brightness, brightness).time(80 + world.rand.nextInt(12)).shaded(true).spawn(world);
brightness = world.rand.nextFloat() * 0.3f;
ParticleBuilder.create(Type.DARK_MAGIC, world.rand, x, y, z, 2 * blastMultiplier, false)
.clr(brightness, brightness, brightness).spawn(world);
}
break;
case PacketBombExplosion.SPARK_BOMB:
ParticleBuilder.spawnShockParticles(world, x, y, z);
for(int id : message.secondaryTargetIDs){
net.minecraft.entity.Entity target = world.getEntityByID(id);
if(target instanceof net.minecraft.entity.EntityLivingBase){
ParticleBuilder.create(Type.LIGHTNING).pos(x, y, z).target(target).spawn(world);
ParticleBuilder.spawnShockParticles(world, target.posX,
target.posY + target.height / 2, target.posZ);
}
}
break;
}
}
// Rendering // Rendering
// =============================================================================================================== // ===============================================================================================================
@@ -938,51 +877,4 @@ public class ClientProxy extends CommonProxy {
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityImbuementAltar.class, new RenderImbuementAltar()); ClientRegistry.bindTileEntitySpecialRenderer(TileEntityImbuementAltar.class, new RenderImbuementAltar());
} }
public void registerItemColorHandlers() {
// Spell book overlay textures are now per-element, so no runtime tinting is needed.
}
public void registerModelProperties() {
Item spellBook = WizardryItems.spell_book;
spellBook.addPropertyOverride(new ResourceLocation(Wizardry.MODID, "discovered"), (stack, world, entity) -> {
if (!Wizardry.settings.spellBookColors) return 0.0f;
boolean discovered = shouldDisplayDiscovered(Spell.byMetadata(stack.getMetadata()), stack);
if(discovered && Wizardry.settings.spellBookColorsRequireArchivistsEyeglass){
// Entity can be null so we have to check
if(entity instanceof EntityPlayer){
return ItemArtefact.isArtefactActive((EntityPlayer)entity, WizardryItems.charm_spell_discovery) ? 1.0f : 0.0f;
}
// If there's no entity, there's no charm, so no colour
return 0.0f;
}
return discovered ? 1.0f : 0.0f;
});
// Returns ordinal+1 for each element (1=MAGIC through 8=HEALING), or 0 if not yet discovered.
// Drives per-element bookmark texture selection via model overrides in spell_book.json.
spellBook.addPropertyOverride(new ResourceLocation(Wizardry.MODID, "element"), (stack, world, entity) -> {
if (!Wizardry.settings.spellBookColors) return 0.0f;
Spell spell = Spell.byMetadata(stack.getMetadata());
boolean discovered = shouldDisplayDiscovered(spell, stack);
if(!discovered) return 0.0f;
if(Wizardry.settings.spellBookColorsRequireArchivistsEyeglass){
if(entity instanceof EntityPlayer){
if(!ItemArtefact.isArtefactActive((EntityPlayer)entity, WizardryItems.charm_spell_discovery)) return 0.0f;
} else {
return 0.0f;
}
}
Element element = spell.getElement();
return element == null ? 0.0f : (float)(element.ordinal() + 1);
});
}
} }
@@ -1,6 +1,5 @@
package electroblob.wizardry.client; package electroblob.wizardry.client;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect; import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
import electroblob.wizardry.data.DispenserCastingData; import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.data.SpellEmitterData; import electroblob.wizardry.data.SpellEmitterData;
@@ -14,6 +13,7 @@ import electroblob.wizardry.spell.SixthSense;
import electroblob.wizardry.spell.SlowTime; import electroblob.wizardry.spell.SlowTime;
import electroblob.wizardry.spell.Transience; import electroblob.wizardry.spell.Transience;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
@@ -31,9 +31,6 @@ import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.Side;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.event.entity.player.ItemTooltipEvent;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import java.util.ArrayList; import java.util.ArrayList;
@@ -199,15 +196,6 @@ public final class WizardryClientEventHandler {
} }
} }
@SubscribeEvent
public static void onItemTooltip(ItemTooltipEvent event) {
if (event.getItemStack().getItem() == WizardryItems.charm_spell_discovery) {
if (Wizardry.settings.spellBookColorsRequireArchivistsEyeglass) {
event.getToolTip().add(TextFormatting.GRAY + I18n.format("item.ebwizardry:charm_spell_discovery.desc.color"));
}
}
}
/** /**
* Renders an overlay across the entire screen. * Renders an overlay across the entire screen.
* @param resolution The screen resolution * @param resolution The screen resolution
@@ -515,13 +515,12 @@ public class GuiArcaneWorkbench extends GuiContainer {
@Override @Override
protected void keyTyped(char typedChar, int keyCode) throws IOException { protected void keyTyped(char typedChar, int keyCode) throws IOException {
if(this.searchNeedsClearing){ if(this.searchNeedsClearing){
this.searchNeedsClearing = false; this.searchNeedsClearing = false;
this.searchField.setText(""); this.searchField.setText("");
} }
// Allow exiting the GUI by pressing the inventory button. Does not work if bookshelves are present by default if(this.searchField.textboxKeyTyped(typedChar, keyCode)){
if(this.searchField.getVisible() && this.searchField.textboxKeyTyped(typedChar, keyCode)){
arcaneWorkbenchContainer.setSearchText(searchField.getText().toLowerCase(Locale.ROOT)); arcaneWorkbenchContainer.setSearchText(searchField.getText().toLowerCase(Locale.ROOT));
}else{ }else{
super.keyTyped(typedChar, keyCode); super.keyTyped(typedChar, keyCode);
@@ -7,6 +7,7 @@ import electroblob.wizardry.enchantment.Imbuement;
import electroblob.wizardry.entity.living.ISummonedCreature; import electroblob.wizardry.entity.living.ISummonedCreature;
import electroblob.wizardry.event.SpellCastEvent; import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.event.SpellCastEvent.Source; import electroblob.wizardry.event.SpellCastEvent.Source;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.packet.PacketCastContinuousSpell; import electroblob.wizardry.packet.PacketCastContinuousSpell;
import electroblob.wizardry.packet.PacketPlayerSync; import electroblob.wizardry.packet.PacketPlayerSync;
import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.packet.WizardryPacketHandler;
@@ -355,20 +356,20 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
// For each item in the player's inventory // For each item in the player's inventory
for(ItemStack stack : player.inventory.mainInventory){ for(ItemStack stack : player.inventory.mainInventory){
updateImbuedItem(stack, activeImbuements); updateImbutedItem(stack, activeImbuements);
} }
for(ItemStack stack : player.inventory.armorInventory){ for(ItemStack stack : player.inventory.armorInventory){
updateImbuedItem(stack, activeImbuements); updateImbutedItem(stack, activeImbuements);
} }
for(ItemStack stack : player.inventory.offHandInventory){ for(ItemStack stack : player.inventory.offHandInventory){
updateImbuedItem(stack, activeImbuements); updateImbutedItem(stack, activeImbuements);
} }
// Removes all imbuements from the map that are no longer active // Removes all imbuements from the map that are no longer active
this.imbuementDurations.keySet().retainAll(activeImbuements); this.imbuementDurations.keySet().retainAll(activeImbuements);
} }
private void updateImbuedItem(ItemStack stack, Set<Imbuement> activeImbuements){ private void updateImbutedItem(ItemStack stack, Set<Imbuement> activeImbuements){
if(stack.isItemEnchanted()){ if(stack.isItemEnchanted()){
@@ -465,7 +466,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
if(this.castCommandSpell != null && this.castCommandSpell.isContinuous){ if(this.castCommandSpell != null && this.castCommandSpell.isContinuous){
if(castCommandTick > castCommandDuration){ if(castCommandTick >= castCommandDuration){
this.stopCastingContinuousSpell(); this.stopCastingContinuousSpell();
return; return;
} }
@@ -1,18 +1,18 @@
package electroblob.wizardry.entity.projectile; package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.packet.PacketBombExplosion;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.EntityUtils; import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.MagicDamage.DamageType; import electroblob.wizardry.util.MagicDamage.DamageType;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import java.util.List; import java.util.List;
@@ -30,26 +30,39 @@ public class EntityFirebomb extends EntityBomb {
@Override @Override
protected void onImpact(RayTraceResult rayTrace){ protected void onImpact(RayTraceResult rayTrace){
if(!this.world.isRemote){ Entity entityHit = rayTrace.entityHit;
Entity entityHit = rayTrace.entityHit; if(entityHit != null){
// This is if the firebomb gets a direct hit
float damage = Spells.firebomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
if(entityHit != null){ entityHit.attackEntityFrom(
// This is if the firebomb gets a direct hit MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(),
float damage = Spells.firebomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier; damage);
entityHit.attackEntityFrom( if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit))
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(), entityHit.setFire(Spells.firebomb.getProperty(Spell.BURN_DURATION).intValue());
damage); }
if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit)) // Particle effect
entityHit.setFire(Spells.firebomb.getProperty(Spell.BURN_DURATION).intValue()); if(world.isRemote){
ParticleBuilder.create(Type.FLASH).pos(this.getPositionVector()).scale(5 * blastMultiplier).clr(1, 0.6f, 0)
.spawn(world);
for(int i = 0; i < 60 * blastMultiplier; i++){
ParticleBuilder.create(Type.MAGIC_FIRE, rand, posX, posY, posZ, 2*blastMultiplier, false)
.time(10 + rand.nextInt(4)).scale(2 + rand.nextFloat()).spawn(world);
ParticleBuilder.create(Type.DARK_MAGIC, rand, posX, posY, posZ, 2*blastMultiplier, false)
.clr(1.0f, 0.2f + rand.nextFloat() * 0.4f, 0.0f).spawn(world);
} }
// Notify clients to play the explosion effect this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
WizardryPacketHandler.net.sendToAllAround( }
new PacketBombExplosion.Message(PacketBombExplosion.FIREBOMB, posX, posY, posZ, blastMultiplier),
new NetworkRegistry.TargetPoint(world.provider.getDimension(), posX, posY, posZ, 64)); if(!this.world.isRemote){
this.playSound(WizardrySounds.ENTITY_FIREBOMB_SMASH, 1.5F, rand.nextFloat() * 0.4F + 0.6F); this.playSound(WizardrySounds.ENTITY_FIREBOMB_SMASH, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(WizardrySounds.ENTITY_FIREBOMB_FIRE, 1, 1); this.playSound(WizardrySounds.ENTITY_FIREBOMB_FIRE, 1, 1);
@@ -69,9 +82,9 @@ public class EntityFirebomb extends EntityBomb {
target.setFire(Spells.firebomb.getProperty(Spell.BURN_DURATION).intValue()); target.setFire(Spells.firebomb.getProperty(Spell.BURN_DURATION).intValue());
} }
} }
}
this.setDead(); this.setDead();
}
} }
} }
@@ -1,20 +1,20 @@
package electroblob.wizardry.entity.projectile; package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.packet.PacketBombExplosion;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.EntityUtils; import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.MagicDamage.DamageType; import electroblob.wizardry.util.MagicDamage.DamageType;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.MobEffects; import net.minecraft.init.MobEffects;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import java.util.List; import java.util.List;
@@ -32,28 +32,42 @@ public class EntityPoisonBomb extends EntityBomb {
@Override @Override
protected void onImpact(RayTraceResult rayTrace){ protected void onImpact(RayTraceResult rayTrace){
if(!this.world.isRemote){ Entity entityHit = rayTrace.entityHit;
Entity entityHit = rayTrace.entityHit; if(entityHit != null){
// This is if the poison bomb gets a direct hit
float damage = Spells.poison_bomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
if(entityHit != null){ entityHit.attackEntityFrom(
// This is if the poison bomb gets a direct hit MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON).setProjectile(),
float damage = Spells.poison_bomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier; damage);
entityHit.attackEntityFrom( if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.POISON, entityHit))
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON).setProjectile(), ((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(MobEffects.POISON,
damage); Spells.poison_bomb.getProperty(Spell.DIRECT_EFFECT_DURATION).intValue(),
Spells.poison_bomb.getProperty(Spell.DIRECT_EFFECT_STRENGTH).intValue()));
}
if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.POISON, entityHit)) // Particle effect
((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(MobEffects.POISON, if(world.isRemote){
Spells.poison_bomb.getProperty(Spell.DIRECT_EFFECT_DURATION).intValue(),
Spells.poison_bomb.getProperty(Spell.DIRECT_EFFECT_STRENGTH).intValue())); ParticleBuilder.create(Type.FLASH).pos(this.getPositionVector()).scale(5 * blastMultiplier)
.clr(0.2f + rand.nextFloat() * 0.3f, 0.6f, 0.0f).spawn(world);
for(int i = 0; i < 60 * blastMultiplier; i++){
ParticleBuilder.create(Type.SPARKLE, rand, posX, posY, posZ, 2*blastMultiplier, false).time(35)
.scale(2).clr(0.2f + rand.nextFloat() * 0.3f, 0.6f, 0.0f).spawn(world);
ParticleBuilder.create(Type.DARK_MAGIC, rand, posX, posY, posZ, 2*blastMultiplier, false)
.clr(0.2f + rand.nextFloat() * 0.2f, 0.8f, 0.0f).spawn(world);
} }
// Spawning this after the other particles fixes the rendering colour bug. It's a bit of a cheat, but it
// works pretty well.
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
// Notify clients to play the explosion effect if(!this.world.isRemote){
WizardryPacketHandler.net.sendToAllAround(
new PacketBombExplosion.Message(PacketBombExplosion.POISON_BOMB, posX, posY, posZ, blastMultiplier),
new NetworkRegistry.TargetPoint(world.provider.getDimension(), posX, posY, posZ, 64));
this.playSound(WizardrySounds.ENTITY_POISON_BOMB_SMASH, 1.5F, rand.nextFloat() * 0.4F + 0.6F); this.playSound(WizardrySounds.ENTITY_POISON_BOMB_SMASH, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(WizardrySounds.ENTITY_POISON_BOMB_POISON, 1.2F, 1.0f); this.playSound(WizardrySounds.ENTITY_POISON_BOMB_POISON, 1.2F, 1.0f);
@@ -74,8 +88,8 @@ public class EntityPoisonBomb extends EntityBomb {
Spells.poison_bomb.getProperty(Spell.SPLASH_EFFECT_STRENGTH).intValue())); Spells.poison_bomb.getProperty(Spell.SPLASH_EFFECT_STRENGTH).intValue()));
} }
} }
}
this.setDead(); this.setDead();
}
} }
} }
@@ -1,17 +1,17 @@
package electroblob.wizardry.entity.projectile; package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.packet.PacketBombExplosion;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.EntityUtils; import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.MobEffects; import net.minecraft.init.MobEffects;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import java.util.List; import java.util.List;
@@ -29,12 +29,26 @@ public class EntitySmokeBomb extends EntityBomb {
@Override @Override
protected void onImpact(RayTraceResult rayTrace){ protected void onImpact(RayTraceResult rayTrace){
if(!this.world.isRemote){ // Particle effect
if(world.isRemote){
// Notify clients to play the explosion effect ParticleBuilder.create(Type.FLASH).pos(this.getPositionVector()).scale(5 * blastMultiplier).clr(0, 0, 0).spawn(world);
WizardryPacketHandler.net.sendToAllAround(
new PacketBombExplosion.Message(PacketBombExplosion.SMOKE_BOMB, posX, posY, posZ, blastMultiplier), this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
new NetworkRegistry.TargetPoint(world.provider.getDimension(), posX, posY, posZ, 64));
for(int i = 0; i < 60 * blastMultiplier; i++){
float brightness = rand.nextFloat() * 0.1f + 0.1f;
ParticleBuilder.create(Type.CLOUD, rand, posX, posY, posZ, 2*blastMultiplier, false)
.clr(brightness, brightness, brightness).time(80 + this.rand.nextInt(12)).shaded(true).spawn(world);
brightness = rand.nextFloat() * 0.3f;
ParticleBuilder.create(Type.DARK_MAGIC, rand, posX, posY, posZ, 2*blastMultiplier, false)
.clr(brightness, brightness, brightness).spawn(world);
}
}
if(!this.world.isRemote){
this.playSound(WizardrySounds.ENTITY_SMOKE_BOMB_SMASH, 1.5F, rand.nextFloat() * 0.4F + 0.6F); this.playSound(WizardrySounds.ENTITY_SMOKE_BOMB_SMASH, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(WizardrySounds.ENTITY_SMOKE_BOMB_SMOKE, 1.2F, 1.0f); this.playSound(WizardrySounds.ENTITY_SMOKE_BOMB_SMOKE, 1.2F, 1.0f);
@@ -51,8 +65,8 @@ public class EntitySmokeBomb extends EntityBomb {
target.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, duration, 0)); target.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, duration, 0));
} }
} }
}
this.setDead(); this.setDead();
}
} }
} }
@@ -1,7 +1,5 @@
package electroblob.wizardry.entity.projectile; package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.packet.PacketBombExplosion;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
@@ -15,9 +13,7 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class EntitySparkBomb extends EntityBomb { public class EntitySparkBomb extends EntityBomb {
@@ -36,36 +32,46 @@ public class EntitySparkBomb extends EntityBomb {
@Override @Override
protected void onImpact(RayTraceResult rayTrace){ protected void onImpact(RayTraceResult rayTrace){
if(!this.world.isRemote){ this.playSound(WizardrySounds.ENTITY_SPARK_BOMB_HIT_BLOCK, 0.5f, 0.5f);
Entity entityHit = rayTrace.entityHit; Entity entityHit = rayTrace.entityHit;
if(entityHit != null){ if(entityHit != null){
// This is if the spark bomb gets a direct hit // This is if the spark bomb gets a direct hit
float damage = Spells.spark_bomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier; float damage = Spells.spark_bomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
this.playSound(WizardrySounds.ENTITY_SPARK_BOMB_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); this.playSound(WizardrySounds.ENTITY_SPARK_BOMB_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
entityHit.attackEntityFrom( entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(), MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(),
damage); damage);
}
double seekerRange = Spells.spark_bomb.getProperty(Spell.EFFECT_RADIUS).doubleValue() * blastMultiplier; }
List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(seekerRange, this.posX, this.posY, // Particle effect
this.posZ, this.world); if(world.isRemote){
ParticleBuilder.spawnShockParticles(world, posX, posY + height/2, posZ);
}
List<Integer> secondaryTargetIDs = new ArrayList<>(); double seekerRange = Spells.spark_bomb.getProperty(Spell.EFFECT_RADIUS).doubleValue() * blastMultiplier;
for(int i = 0; i < Math.min(targets.size(), Spells.spark_bomb.getProperty(SECONDARY_MAX_TARGETS).intValue()); i++){ List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(seekerRange, this.posX, this.posY,
this.posZ, this.world);
boolean flag = targets.get(i) != entityHit && targets.get(i) != this.getThrower() for(int i = 0; i < Math.min(targets.size(), Spells.spark_bomb.getProperty(SECONDARY_MAX_TARGETS).intValue()); i++){
&& !(targets.get(i) instanceof EntityPlayer
&& ((EntityPlayer)targets.get(i)).isCreative());
if(flag){ boolean flag = targets.get(i) != entityHit && targets.get(i) != this.getThrower()
EntityLivingBase target = targets.get(i); && !(targets.get(i) instanceof EntityPlayer
&& ((EntityPlayer)targets.get(i)).isCreative());
// Detects (client side) if target is the thrower, to stop particles being spawned around them.
//if(flag && world.isRemote && targets.get(i).getEntityId() == this.playerID) flag = false;
if(flag){
EntityLivingBase target = targets.get(i);
if(!this.world.isRemote){
target.playSound(WizardrySounds.ENTITY_SPARK_BOMB_CHAIN, 1.0F, rand.nextFloat() * 0.4F + 1.5F); target.playSound(WizardrySounds.ENTITY_SPARK_BOMB_CHAIN, 1.0F, rand.nextFloat() * 0.4F + 1.5F);
@@ -73,18 +79,11 @@ public class EntitySparkBomb extends EntityBomb {
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK), MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK),
Spells.spark_bomb.getProperty(Spell.SPLASH_DAMAGE).floatValue() * damageMultiplier); Spells.spark_bomb.getProperty(Spell.SPLASH_DAMAGE).floatValue() * damageMultiplier);
secondaryTargetIDs.add(target.getEntityId()); }else{
ParticleBuilder.create(Type.LIGHTNING).pos(this.getPositionVector()).target(target).spawn(world);
ParticleBuilder.spawnShockParticles(world, target.posX, target.posY + target.height/2, target.posZ);
} }
} }
// Notify clients to play the explosion effect, including lightning arcs to secondary targets
int[] idArray = secondaryTargetIDs.stream().mapToInt(Integer::intValue).toArray();
WizardryPacketHandler.net.sendToAllAround(
new PacketBombExplosion.Message(PacketBombExplosion.SPARK_BOMB, posX, posY + height / 2, posZ,
blastMultiplier, idArray),
new NetworkRegistry.TargetPoint(world.provider.getDimension(), posX, posY, posZ, 64));
this.playSound(WizardrySounds.ENTITY_SPARK_BOMB_HIT_BLOCK, 0.5f, 0.5f);
} }
this.setDead(); this.setDead();
@@ -160,21 +160,12 @@ public class ItemScroll extends Item implements ISpellCastingItem, IWorkbenchIte
Spell spell = Spell.byMetadata(stack.getItemDamage()); Spell spell = Spell.byMetadata(stack.getItemDamage());
// By default, scrolls have no modifiers - but with the event system, they could be added. // By default, scrolls have no modifiers - but with the event system, they could be added.
SpellModifiers modifiers = new SpellModifiers();
SpellModifiers modifiers;
if(WizardData.get(player) != null){
modifiers = WizardData.get(player).itemCastingModifiers;
}else{
modifiers = new SpellModifiers();
}
int castingTick = stack.getMaxItemUseDuration() - count; int castingTick = stack.getMaxItemUseDuration() - count;
// Continuous spells (these must check if they can be cast each tick since the mana changes) // Continuous spells (these must check if they can be cast each tick since the mana changes)
// In theory the spell is always continuous here but just in case it isn't... // In theory the spell is always continuous here but just in case it isn't...
// Do not check canCast() on tick 0 as it is already done in onItemRightClick() and would duplicate modifiers if(spell.isContinuous && canCast(stack, spell, player, player.getActiveHand(), castingTick, modifiers)){
if(spell.isContinuous && (castingTick == 0 || canCast(stack, spell, player, player.getActiveHand(), castingTick, modifiers))){
cast(stack, spell, player, player.getActiveHand(), castingTick, modifiers); cast(stack, spell, player, player.getActiveHand(), castingTick, modifiers);
}else{ }else{
// Scrolls normally work on the max use duration so this isn't ever reached by wizardry, but if the // Scrolls normally work on the max use duration so this isn't ever reached by wizardry, but if the
@@ -1,79 +0,0 @@
package electroblob.wizardry.packet;
import electroblob.wizardry.Wizardry;
import io.netty.buffer.ByteBuf;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
/**
* <b>[Server -> Client]</b> This packet is sent when a bomb entity impacts something. It is used to guarantee that the
* client always sees the explosion effect, even in singleplayer where the server may remove the entity before the
* client's own simulation can call {@code onImpact()}.
*/
public class PacketBombExplosion implements IMessageHandler<PacketBombExplosion.Message, IMessage> {
/** Bomb type constants, matching the order bombs are handled in {@link electroblob.wizardry.CommonProxy}. */
public static final int FIREBOMB = 0;
public static final int POISON_BOMB = 1;
public static final int SMOKE_BOMB = 2;
public static final int SPARK_BOMB = 3;
@Override
public IMessage onMessage(Message message, MessageContext ctx){
if(ctx.side.isClient()){
net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(
() -> Wizardry.proxy.handleBombExplosionPacket(message));
}
return null;
}
public static class Message implements IMessage {
/** One of the bomb type constants defined in {@link PacketBombExplosion}. */
public int bombType;
public double x, y, z;
public float blastMultiplier;
/** Entity IDs of secondary targets struck by the spark bomb chain. Empty for other bomb types. */
public int[] secondaryTargetIDs;
// Required no-arg constructor
public Message(){}
public Message(int bombType, double x, double y, double z, float blastMultiplier){
this(bombType, x, y, z, blastMultiplier, new int[0]);
}
public Message(int bombType, double x, double y, double z, float blastMultiplier, int[] secondaryTargetIDs){
this.bombType = bombType;
this.x = x;
this.y = y;
this.z = z;
this.blastMultiplier = blastMultiplier;
this.secondaryTargetIDs = secondaryTargetIDs;
}
@Override
public void fromBytes(ByteBuf buf){
bombType = buf.readInt();
x = buf.readDouble();
y = buf.readDouble();
z = buf.readDouble();
blastMultiplier = buf.readFloat();
int count = buf.readInt();
secondaryTargetIDs = new int[count];
for(int i = 0; i < count; i++) secondaryTargetIDs[i] = buf.readInt();
}
@Override
public void toBytes(ByteBuf buf){
buf.writeInt(bombType);
buf.writeDouble(x);
buf.writeDouble(y);
buf.writeDouble(z);
buf.writeFloat(blastMultiplier);
buf.writeInt(secondaryTargetIDs.length);
for(int id : secondaryTargetIDs) buf.writeInt(id);
}
}
}
@@ -35,7 +35,6 @@ public class WizardryPacketHandler {
registerMessage(PacketSpellQuickAccess.class, PacketSpellQuickAccess.Message.class); registerMessage(PacketSpellQuickAccess.class, PacketSpellQuickAccess.Message.class);
registerMessage(PacketRequestDonationPerks.class, PacketRequestDonationPerks.Message.class); registerMessage(PacketRequestDonationPerks.class, PacketRequestDonationPerks.Message.class);
registerMessage(PacketSyncDonationPerks.class, PacketSyncDonationPerks.Message.class); registerMessage(PacketSyncDonationPerks.class, PacketSyncDonationPerks.Message.class);
registerMessage(PacketBombExplosion.class, PacketBombExplosion.Message.class);
} }
private static int nextPacketId = 0; private static int nextPacketId = 0;
@@ -907,7 +907,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
* its JSON file using a resource pack. If called with no arguments, defaults to any context, i.e. only returns * its JSON file using a resource pack. If called with no arguments, defaults to any context, i.e. only returns
* false if the spell is completely disabled in all contexts. */ * false if the spell is completely disabled in all contexts. */
public final boolean isEnabled(SpellProperties.Context... contexts){ public final boolean isEnabled(SpellProperties.Context... contexts){
return enabled && (contexts.length == 0 || (arePropertiesInitialised() && properties.isEnabled(contexts))); return enabled && (contexts.length == 0 || properties.isEnabled(contexts));
} }
/** Sets whether the spell is enabled or not. */ /** Sets whether the spell is enabled or not. */
@@ -235,11 +235,12 @@ public class TileEntityImbuementAltar extends TileEntity implements ITickable {
*/ */
public static ItemStack getImbuementResult(ItemStack input, Element[] receptacleElements, boolean fullLootGen, World world, EntityPlayer lastUser){ public static ItemStack getImbuementResult(ItemStack input, Element[] receptacleElements, boolean fullLootGen, World world, EntityPlayer lastUser){
ImbuementActivateEvent event = new ImbuementActivateEvent(input, receptacleElements, world, lastUser, ItemStack.EMPTY); ItemStack eventResult = ItemStack.EMPTY;
if (world != null && MinecraftForge.EVENT_BUS.post(event)) { if (world != null && MinecraftForge.EVENT_BUS.post(new ImbuementActivateEvent(input, receptacleElements, world, lastUser, eventResult))) {
// Event was cancelled; return whatever result the handler set (may be empty to suppress all recipes). // return the stack if something changes the result from an empty stack.
return event.result; //noinspection ConstantConditions
if (eventResult != ItemStack.EMPTY) return eventResult;
} }
if(input.getItem() instanceof ItemWizardArmour && ((ItemWizardArmour)input.getItem()).element == null){ if(input.getItem() instanceof ItemWizardArmour && ((ItemWizardArmour)input.getItem()).element == null){
@@ -117,9 +117,6 @@ public final class AllyDesignationSystem {
// Always return true if the attacker is null - this must be after the target null check! // Always return true if the attacker is null - this must be after the target null check!
if(attacker == null) return true; if(attacker == null) return true;
// Teammates are allies
if(attacker.isOnSameTeam(target)) return false;
// Tests whether the target is the attacker // Tests whether the target is the attacker
if(target == attacker) return false; if(target == attacker) return false;
@@ -206,10 +203,6 @@ public final class AllyDesignationSystem {
* generally used to determine targets for healing or other group buffs. */ * generally used to determine targets for healing or other group buffs. */
public static boolean isAllied(EntityLivingBase allyOf, EntityLivingBase possibleAlly){ public static boolean isAllied(EntityLivingBase allyOf, EntityLivingBase possibleAlly){
if (allyOf == null || possibleAlly == null) {
return false;
}
// Owned entities inherit their owner's allies // Owned entities inherit their owner's allies
if(allyOf instanceof IEntityOwnable){ if(allyOf instanceof IEntityOwnable){
Entity owner = ((IEntityOwnable)allyOf).getOwner(); Entity owner = ((IEntityOwnable)allyOf).getOwner();
@@ -227,10 +220,6 @@ public final class AllyDesignationSystem {
if(allyOf instanceof EntityPlayer && isOwnerAlly((EntityPlayer)allyOf, pet)) return true; if(allyOf instanceof EntityPlayer && isOwnerAlly((EntityPlayer)allyOf, pet)) return true;
} }
if(possibleAlly.isOnSameTeam(allyOf)) {
return true;
}
// Check if the possibleAlly is mind controlled by the allyOf entity // Check if the possibleAlly is mind controlled by the allyOf entity
if(possibleAlly instanceof EntityLiving && possibleAlly.isPotionActive(WizardryPotions.mind_control)){ if(possibleAlly instanceof EntityLiving && possibleAlly.isPotionActive(WizardryPotions.mind_control)){
NBTTagCompound entityNBT = possibleAlly.getEntityData(); NBTTagCompound entityNBT = possibleAlly.getEntityData();
@@ -524,7 +524,6 @@ item.ebwizardry\:charm_move_speed.name=Icarus Medallion
item.ebwizardry\:charm_move_speed.desc=Move at near-full speed when casting spells item.ebwizardry\:charm_move_speed.desc=Move at near-full speed when casting spells
item.ebwizardry\:charm_spell_discovery.name=Archivist's Eyeglass item.ebwizardry\:charm_spell_discovery.name=Archivist's Eyeglass
item.ebwizardry\:charm_spell_discovery.desc=Spell books found in chests are much more likely to be ones you have not yet discovered item.ebwizardry\:charm_spell_discovery.desc=Spell books found in chests are much more likely to be ones you have not yet discovered
item.ebwizardry\:charm_spell_discovery.desc.color=When equipped, you can see the element of a known spell book by the color of its bookmark
item.ebwizardry\:charm_auto_smelt.name=Metallurgist's Mark item.ebwizardry\:charm_auto_smelt.name=Metallurgist's Mark
item.ebwizardry\:charm_auto_smelt.desc=Pocket furnace triggers automatically when bound to a wand on your hotbar item.ebwizardry\:charm_auto_smelt.desc=Pocket furnace triggers automatically when bound to a wand on your hotbar
item.ebwizardry\:charm_lava_walking.name=Nether Ice Core item.ebwizardry\:charm_lava_walking.name=Nether Ice Core
@@ -1693,10 +1692,6 @@ config.ebwizardry.replace_vanilla_fall_damage=Replace Vanilla Fall Damage
config.ebwizardry.replace_vanilla_fall_damage.tooltip=Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells revert to a more simplistic method of resetting the player's fall damage in certain cases. config.ebwizardry.replace_vanilla_fall_damage.tooltip=Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells revert to a more simplistic method of resetting the player's fall damage in certain cases.
config.ebwizardry.replace_vanilla_fall_damage.true=Yes - it's parkour time config.ebwizardry.replace_vanilla_fall_damage.true=Yes - it's parkour time
config.ebwizardry.replace_vanilla_fall_damage.false=No - break my legs normally config.ebwizardry.replace_vanilla_fall_damage.false=No - break my legs normally
config.ebwizardry.spell_book_colors_require_charm=Spell Book Colors Require Archivist's Eyeglass
config.ebwizardry.spell_book_colors_require_charm.tooltip=Whether to require the Archivist's Eyeglass charm artifact to see colored spell book bookmarks.
config.ebwizardry.spell_book_colors_require_charm.true=Yes - I want to see the colours only if the charm is worn
config.ebwizardry.spell_book_colors_require_charm.false=No - I want to see the colours all the time
config.ebwizardry.blindness_tweak=Blindness Tweak config.ebwizardry.blindness_tweak=Blindness Tweak
config.ebwizardry.blindness_tweak.tooltip=Whether to tweak the blindness effect to reduce follow distance when used on non-players. This automatically disables itself in favour of Potion Core's implementation if installed. config.ebwizardry.blindness_tweak.tooltip=Whether to tweak the blindness effect to reduce follow distance when used on non-players. This automatically disables itself in favour of Potion Core's implementation if installed.
config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist
@@ -1,14 +1,14 @@
{ {
"parent": "ebwizardry:item/spell_book_undiscovered", "parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book"
},
"overrides": [ "overrides": [
{"predicate": {"ebwizardry:element": 1}, "model": "ebwizardry:item/spell_book_discovered_magic"}, {
{"predicate": {"ebwizardry:element": 2}, "model": "ebwizardry:item/spell_book_discovered_fire"}, "predicate": {
{"predicate": {"ebwizardry:element": 3}, "model": "ebwizardry:item/spell_book_discovered_ice"}, "festive": 1
{"predicate": {"ebwizardry:element": 4}, "model": "ebwizardry:item/spell_book_discovered_lightning"}, },
{"predicate": {"ebwizardry:element": 5}, "model": "ebwizardry:item/spell_book_discovered_necromancy"}, "model": "ebwizardry:item/festive_spell_book"
{"predicate": {"ebwizardry:element": 6}, "model": "ebwizardry:item/spell_book_discovered_earth"}, }
{"predicate": {"ebwizardry:element": 7}, "model": "ebwizardry:item/spell_book_discovered_sorcery"},
{"predicate": {"ebwizardry:element": 8}, "model": "ebwizardry:item/spell_book_discovered_healing"},
{"predicate": {"festive": 1}, "model": "ebwizardry:item/festive_spell_book"}
] ]
} }
@@ -1,7 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book",
"layer1": "ebwizardry:items/spell_book_overlay"
}
}
@@ -1,7 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book",
"layer1": "ebwizardry:items/spell_book_overlay_earth"
}
}
@@ -1,7 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book",
"layer1": "ebwizardry:items/spell_book_overlay_fire"
}
}
@@ -1,7 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book",
"layer1": "ebwizardry:items/spell_book_overlay_healing"
}
}
@@ -1,7 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book",
"layer1": "ebwizardry:items/spell_book_overlay_ice"
}
}
@@ -1,7 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book",
"layer1": "ebwizardry:items/spell_book_overlay_lightning"
}
}
@@ -1,7 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book",
"layer1": "ebwizardry:items/spell_book_overlay_magic"
}
}
@@ -1,7 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book",
"layer1": "ebwizardry:items/spell_book_overlay_necromancy"
}
}
@@ -1,7 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book",
"layer1": "ebwizardry:items/spell_book_overlay_sorcery"
}
}
@@ -1,6 +0,0 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 B