Compare commits

...

71 Commits

Author SHA1 Message Date
Electroblob77 807fc61541 Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2019-12-22 18:39:45 +00:00
Electroblob77 9741c751b3 Update version number 2019-12-22 18:37:25 +00:00
Electroblob d3003ae473 Merge pull request #311 from Azim-Palmer/1.12.2
Adding support for Lycanites Mobs to drop loot from the mob_additions table
2019-12-22 18:25:52 +00:00
Electroblob77 9fde24f341 Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2019-12-22 15:15:40 +00:00
Electroblob 452eb17b5f Update question.md 2019-12-22 15:13:57 +00:00
Electroblob 36963aac30 Update feature_request.md 2019-12-22 15:13:47 +00:00
Electroblob f161cdfe7c Update bug_report.md 2019-12-22 15:13:36 +00:00
Electroblob77 455c5a5e07 Fix weird floaty rounding so continuous spells cost the correct amount of mana
Grapple ain't free anymore, sorry folks!
2019-12-22 14:58:12 +00:00
Electroblob77 9852f61d0f Extend smoke bomb blindness effect to mobs as well as players, closes #324 2019-12-22 14:37:19 +00:00
Electroblob77 a2c8cde4c4 Use correct mana values for grand crystals and crystal shards in scroll creation 2019-12-22 12:30:04 +00:00
Electroblob77 497d28eafd Somehow this went missing... 2019-12-22 11:45:42 +00:00
Electroblob77 f3f3377c4d Fix formatting 2019-12-17 18:02:15 +00:00
Electroblob77 4c81c61644 Update credits 2019-12-17 18:01:04 +00:00
Electroblob77 31a6ab6db0 Update version number 2019-12-17 17:57:38 +00:00
Electroblob77 69f2aedef7 Correctly hide/unhide skin overlay parts when rendering frost layer, fixes #261 2019-12-17 17:52:44 +00:00
Electroblob77 e8818a4db5 Correct requested tag type for tower blocks list in wizard data loading, fixes #291 2019-12-17 17:45:27 +00:00
Electroblob77 470a7e82bc Iterate over copy of loaded TE list, fixes #298 and #304 2019-12-17 17:12:31 +00:00
Electroblob77 6439c233f1 Add creative mode check to target selector for summoned creatures, fixes #262 2019-12-17 16:55:00 +00:00
Electroblob77 c931d42d62 Tweaks to loot rarity 2019-12-17 16:35:34 +00:00
Electroblob77 5b497e107a Reinstate max damage check, fixes #294 2019-12-17 16:31:42 +00:00
Electroblob77 324094f418 'Tis the season!
- Decked the halls
- Jingled the bells
- Wrapped the presents
- Loaded up the sleigh
2019-12-17 15:53:40 +00:00
Electroblob77 9ade6442b2 Add config option to turn off growing crystal flowers with bonemeal, closes #308 2019-12-16 15:53:32 +00:00
Electroblob77 ffea49bc40 Copy neat fix for negative scaling of magic light on desync to black hole 2019-12-16 15:39:23 +00:00
Electroblob77 2b89a890d0 Route blink effect through the proxies and make the blink spell check for the first-person player, fixes #280 2019-12-16 15:25:15 +00:00
Electroblob77 3a67388ff3 Don't need that 2019-12-16 15:20:15 +00:00
Electroblob77 3468bbb073 Oops 2019-12-16 13:12:24 +00:00
Electroblob77 963178c609 Cleanup 2019-12-16 13:10:57 +00:00
Electroblob77 cfdb049368 Add failsafe for conjured item max damage, fixes #288 2019-12-16 13:06:32 +00:00
Electroblob77 058d83fd00 Stop ring of chaining attacking its wearer, fixes #303 2019-12-16 12:36:37 +00:00
Electroblob77 c7ccc37dca Add damage property to frost axe spell, fixes #322 2019-12-16 12:33:48 +00:00
Electroblob77 74345d0c3b Check if list is empty after removing current location, fixes #307 2019-12-16 12:18:46 +00:00
Electroblob77 907b94c74f Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2019-12-16 12:14:47 +00:00
Electroblob77 2c6b397be6 Check for null tile entities when rendering arcane lock, fixes #305 2019-12-16 12:14:43 +00:00
Electroblob77 eddcb2402d Don't use client-side-only vector-based AxisAlignedBB constructor, fixes #306 2019-12-16 12:14:01 +00:00
Electroblob 0301f3c300 Merge pull request #312 from Azim-Palmer/Fix/LayerRender
Fix : LayerRender breaks on non-standard models
2019-12-16 00:31:34 +00:00
Electroblob 7a8cfb73cd Merge pull request #319 from UltraHex/1.12.2
Fixes #170
2019-12-16 00:29:47 +00:00
Matthew William Noel c7c6f964bf Fix Electroblob77/Wizardry#170 the right way 2019-12-07 12:46:40 -08:00
Matthew William Noel 91df7cfd72 Fix Electroblob77/Wizardry#170 2019-12-06 19:54:55 -08:00
Az 0ed9ee64c4 Fix : when rendering models that don't have any entries in the boxList (Such as Lycanite's mobs) this function throws and exception and the renderer goes crazy. 2019-11-23 19:25:23 +00:00
Az f82d2871d5 Adding support for Lycanites Mobs to drop loot from the mob_additions table 2019-11-23 12:44:10 +00:00
Electroblob77 01cea3df06 Update version number 2019-10-05 23:14:29 +01:00
Electroblob77 591cfb5857 Check we're dealing with the type of mob whose class we're in, fixes #274 2019-10-05 23:13:48 +01:00
Electroblob77 bc2037f74e Update version number 2019-10-05 17:15:56 +01:00
Electroblob77 6aa3b21190 Convert all in-world sounds from stereo to mono, fixes #218 2019-10-05 17:12:33 +01:00
Electroblob77 b262b6edc0 Oops 2019-10-05 17:04:26 +01:00
Electroblob77 b7be05669b Increase health threshold for amulet of transience from 2 to 6, fixes #192 2019-10-05 16:43:17 +01:00
Electroblob77 071e299d48 Set evoker spell timer as well as spell type when under the arcane jammer effect, fixes #244 2019-10-05 16:40:57 +01:00
Electroblob77 1e885d70c3 Gently encourage people to disable structures the intended way, and stop it crashing if they try and do it the wrong way.
What's wrong with my structures anyway? :'(
2019-10-05 16:28:24 +01:00
Electroblob77 e04a43e56d Why on earth was that there?! Fixes #257 2019-10-05 15:46:05 +01:00
Electroblob77 378796cdda Make fast worldgen check the central position for water rather than checking the biome 2019-10-05 15:34:41 +01:00
Electroblob77 48c37d1c17 Helps if you actually use the synchronised random... fixes #245 2019-10-05 15:24:20 +01:00
Electroblob77 fe8f9fc5cd Update javadoc 2019-10-05 15:23:01 +01:00
Electroblob77 ced6ab7e8d Use a custom mesh definition instead of registering models for each individual metadata, fixes #256
#256 was partially fixed in 60a6c51; this completes the fix by preventing spell books missing models when different worlds have different spell metadata
2019-10-05 14:40:03 +01:00
Electroblob77 378e02eb6c Ignore special spawn conditions for mobs from mob spawners, fixes #260 2019-10-05 14:34:29 +01:00
Electroblob77 4677d9ff30 Unleash the full potential of spell properties files!
- Spell properties files can now be overridden globally in config/ebwizardry/spells
- Spell properties files can now be overridden for specific worlds in [world save]/data/spells
- Moved the main config file to the new ebwizardry folder because it makes more sense for it to be there now
- Probably broke something in the process
2019-10-02 20:13:44 +01:00
Electroblob77 0541b2171b Even more tweaks to progression 2019-10-02 20:04:17 +01:00
Electroblob77 dc59a14e82 Update .gitignore 2019-10-02 20:03:31 +01:00
Electroblob77 1a13afb6e3 Don't pass null into leaf update tick, fixes #186 and #269 2019-10-01 23:06:47 +01:00
Electroblob77 03dd76c18a Fix the blockpos passed into the block being the blockpos a block below the blockpos of the blockstate where the block was from, fixes #267
Easy mistake to make, but how has it avoided detection for so long...?
2019-09-30 11:30:53 +01:00
Electroblob77 9426a96df3 Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2019-09-27 23:16:07 +01:00
Electroblob 382faf59c9 Update README.md 2019-09-27 23:15:53 +01:00
Electroblob cab68e23ac Rename guide_for_contributing.md to CONTRIBUTING.md 2019-09-27 23:14:43 +01:00
Electroblob77 60a6c5124e API improvements to spell books:
- Allow spells to set which book/scroll items they can appear on
- Allow spell books to set custom GUI textures
- Tweaks to loot functions and wizards to accommodate the changes
2019-09-27 15:46:09 +01:00
Electroblob77 028b753be6 Cleanup 2019-09-27 15:30:15 +01:00
Electroblob77 d020668e0c Update crystal ore picture in handbook 2019-09-27 12:57:55 +01:00
Electroblob77 656a78962e Rebalance progression yet again 2019-09-27 12:57:27 +01:00
Electroblob77 5a2f5aa295 Reimplement looting spell context 2019-09-27 12:55:31 +01:00
Electroblob 5e37a2322c Update CREDITS.md 2019-09-26 11:37:04 +01:00
Electroblob 3ca9f200fd Update README.md 2019-09-13 15:03:04 +01:00
Electroblob 37e3a842f5 Update README.md 2019-09-13 15:02:12 +01:00
Electroblob 4e8e2c951b Update README.md 2019-09-10 00:14:13 +01:00
129 changed files with 738 additions and 267 deletions
+2 -2
View File
@@ -7,10 +7,10 @@ assignees: ''
--- ---
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting. Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting.
Minecraft version: 1.12.2 [change as necessary] Minecraft version: 1.12.2 [change as necessary]
Wizardry version: 4.2.2 [change as necessary] Wizardry version: 4.2.7 [change as necessary]
Environment: Singleplayer/LAN game/Server [delete as necessary] Environment: Singleplayer/LAN game/Server [delete as necessary]
Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.] Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.]
+1 -1
View File
@@ -7,7 +7,7 @@ assignees: ''
--- ---
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting. Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting.
Minecraft versions: [if this suggestion only applies to some versions, list them here] Minecraft versions: [if this suggestion only applies to some versions, list them here]
+2 -2
View File
@@ -7,10 +7,10 @@ assignees: ''
--- ---
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting. Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting.
Minecraft version: 1.12.2 [change as necessary] Minecraft version: 1.12.2 [change as necessary]
Wizardry version: 4.2.2 [change as necessary] Wizardry version: 4.2.7 [change as necessary]
Question details: [explain your question here] Question details: [explain your question here]
+1
View File
@@ -23,6 +23,7 @@ run
logs logs
/misc /misc
/libs /libs
libs_src
libsfordeobf libsfordeobf
generated generated
Thumbs.db Thumbs.db
+3 -1
View File
@@ -1,7 +1,7 @@
# Credits # Credits
Electroblob's Wizardry Electroblob's Wizardry
Version 4.2.3 Version 4.2.7
For Minecraft 1.12.2 For Minecraft 1.12.2
Designed, coded and textured by Electroblob Designed, coded and textured by Electroblob
@@ -21,6 +21,8 @@ In addition, I'd like to thank the following individuals for their contributions
- Tora-B - Tora-B
- Avatair - Avatair
- Aeronica - Aeronica
- UltraHex
- Azim-Palmer
#### Translations #### Translations
+1 -1
View File
@@ -1,7 +1,7 @@
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting. Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting.
Minecraft version: 1.12.2 [change as necessary] Minecraft version: 1.12.2 [change as necessary]
Wizardry version: 4.1.4 [change as necessary] Wizardry version: 4.2.7 [change as necessary]
Environment: Singleplayer/LAN game/Server [delete as necessary] Environment: Singleplayer/LAN game/Server [delete as necessary]
Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.] Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.]
+3 -3
View File
@@ -1,10 +1,10 @@
# Wizardry  [![Curseforge](http://cf.way2muchnoise.eu/full_electroblobs-wizardry_downloads.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry) [![Curseforge](http://cf.way2muchnoise.eu/versions/For%20MC_electroblobs-wizardry_all.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry/files) [![Packs](https://images1-focus-opensocial.googleusercontent.com/gadgets/proxy?container=focus&url=https://cf.way2muchnoise.eu/packs/full_electroblobs-wizardry_in_packs.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry/relations/dependents) [![Discord](https://img.shields.io/discord/417798297265242145.svg?colorB=7289DA&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAABWAgMAAABnZYq0AAAACVBMVEUAAB38%2FPz%2F%2F%2F%2Bm8P%2F9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfhBxwQJhxy2iqrAAABoElEQVRIx7WWzdGEIAyGgcMeKMESrMJ6rILZCiiBg4eYKr%2Fd1ZAfgXFm98sJfAyGNwno3G9sLucgYGpQ4OGVRxQTREMDZjF7ILSWjoiHo1n%2BE03Aw8p7CNY5IhkYd%2F%2F6MtO3f8BNhR1QWnarCH4tr6myl0cWgUVNcfMcXACP1hKrGMt8wcAyxide7Ymcgqale7hN6846uJCkQxw6GG7h2MH4Czz3cLqD1zHu0VOXMfZjHLoYvsdd0Q7ZvsOkafJ1P4QXxrWFd14wMc60h8JKCbyQvImzlFjyGoZTKzohwWR2UzSONHhYXBQOaKKsySsahwGGDnb%2FiYPJw22sCqzirSULYy1qtHhXGbtgrM0oagBV4XiTJok3GoLoDNH8ooTmBm7ZMsbpFzi2bgPGoXWXME6XT%2BRJ4GLddxJ4PpQy7tmfoU2HPN6cKg%2BledKHBKlF8oNSt5w5g5o8eXhu1IOlpl5kGerDxIVT%2BztzKepulD8utXqpChamkzzuo7xYGk%2FkpSYuviLXun5bzdRf0Krejzqyz7Z3p0I1v2d6HmA07dofmS48njAiuMgAAAAASUVORK5CYII%3D)](https://discord.gg/MTmMzMv) # Wizardry  [![Curseforge](http://cf.way2muchnoise.eu/full_electroblobs-wizardry_downloads.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry) [![Curseforge](http://cf.way2muchnoise.eu/versions/For%20MC_electroblobs-wizardry_all.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry/files) [![Discord](https://img.shields.io/discord/417798297265242145.svg?colorB=7289DA&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAABWAgMAAABnZYq0AAAACVBMVEUAAB38%2FPz%2F%2F%2F%2Bm8P%2F9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfhBxwQJhxy2iqrAAABoElEQVRIx7WWzdGEIAyGgcMeKMESrMJ6rILZCiiBg4eYKr%2Fd1ZAfgXFm98sJfAyGNwno3G9sLucgYGpQ4OGVRxQTREMDZjF7ILSWjoiHo1n%2BE03Aw8p7CNY5IhkYd%2F%2F6MtO3f8BNhR1QWnarCH4tr6myl0cWgUVNcfMcXACP1hKrGMt8wcAyxide7Ymcgqale7hN6846uJCkQxw6GG7h2MH4Czz3cLqD1zHu0VOXMfZjHLoYvsdd0Q7ZvsOkafJ1P4QXxrWFd14wMc60h8JKCbyQvImzlFjyGoZTKzohwWR2UzSONHhYXBQOaKKsySsahwGGDnb%2FiYPJw22sCqzirSULYy1qtHhXGbtgrM0oagBV4XiTJok3GoLoDNH8ooTmBm7ZMsbpFzi2bgPGoXWXME6XT%2BRJ4GLddxJ4PpQy7tmfoU2HPN6cKg%2BledKHBKlF8oNSt5w5g5o8eXhu1IOlpl5kGerDxIVT%2BztzKepulD8utXqpChamkzzuo7xYGk%2FkpSYuviLXun5bzdRf0Krejzqyz7Z3p0I1v2d6HmA07dofmS48njAiuMgAAAAASUVORK5CYII%3D)](https://discord.gg/MTmMzMv)
![Image not found](https://media-elerium.cursecdn.com/attachments/203/387/banner7.png) ![Image not found](https://raw.githubusercontent.com/wiki/Electroblob77/Wizardry/images/banners/art_banner.png)
Source code for Electroblob's Wizardry, a Minecraft mod currently for versions 1.7.10, 1.10.2, 1.11.2, and 1.12.2. Wizardry adds an RPG-style system of magic spells to Minecraft with the aim of being as playable as possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master the arcane! Source code for Electroblob's Wizardry, a Minecraft mod currently for versions 1.7.10, 1.10.2, 1.11.2, and 1.12.2. Wizardry adds an RPG-style system of magic spells to Minecraft with the aim of being as playable as possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master the arcane!
Please read the [guide for contributing](guide_for_contributing.md) before submitting an issue or a PR. Please read the [guide for contributing](CONTRIBUTING.md) before submitting an issue or a PR.
<b>Links</b> <b>Links</b>
+1 -1
View File
@@ -11,7 +11,7 @@ apply plugin: 'net.minecraftforge.gradle.forge'
//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. //Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
version = "4.2.3" // There, it matches semver, happy now? version = "4.2.7" // There, it matches semver, happy now?
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"
@@ -7,6 +7,7 @@ import electroblob.wizardry.spell.Spell;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundCategory;
@@ -238,6 +239,9 @@ public class CommonProxy {
+ "of sound events, but the given array contained less than 3 sound events!"); + "of sound events, but the given array contained less than 3 sound events!");
playSpellSoundLoop(world, x, y, z, spell, sounds[0], sounds[1], sounds[2], category, volume, pitch, duration); playSpellSoundLoop(world, x, y, z, spell, sounds[0], sounds[1], sounds[2], category, volume, pitch, duration);
} }
/** Starts the first-person blink overlay effect for the specified player. */
public void playBlinkEffect(EntityPlayer player){}
/** /**
* Gets the client side world using Minecraft.getMinecraft().world. <b>Only to be called client side!</b> Returns * Gets the client side world using Minecraft.getMinecraft().world. <b>Only to be called client side!</b> Returns
@@ -17,6 +17,7 @@ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.Side;
import java.io.File;
import java.util.*; import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -168,6 +169,8 @@ public final class Settings {
/** <b>[Server-only]</b> Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, /** <b>[Server-only]</b> Whether to replace Minecraft's distance-based fall damage calculation with an equivalent,
* velocity-based one. */ * velocity-based one. */
public boolean replaceVanillaFallDamage = true; public boolean replaceVanillaFallDamage = true;
/** <b>[Server-only]</b> Whether using bonemeal on grass blocks has a chance to grow crystal flowers. */
public boolean bonemealGrowsCrystalFlowers = true;
/** /**
* <b>[Server-only]</b> List of registry names of entities which summoned creatures are allowed to attack, in addition * <b>[Server-only]</b> List of registry names of entities which summoned creatures are allowed to attack, in addition
* to the defaults. * to the defaults.
@@ -364,7 +367,7 @@ public final class Settings {
*/ */
void initConfig(FMLPreInitializationEvent event){ void initConfig(FMLPreInitializationEvent event){
config = new Configuration(event.getSuggestedConfigurationFile()); config = new Configuration(new File(Wizardry.configDirectory, Wizardry.MODID + ".cfg"));
config.load(); config.load();
Wizardry.logger.info("Setting up main config"); Wizardry.logger.info("Setting up main config");
@@ -541,6 +544,13 @@ public final class Settings {
slowTimeAffectsPlayers = property.getBoolean(); slowTimeAffectsPlayers = property.getBoolean();
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "bonemealGrowsCrystalFlowers", true,
"Whether using bonemeal on grass blocks has a chance to grow crystal flowers.");
property.setLanguageKey("config." + Wizardry.MODID + ".bonemeal_grows_crystal_flowers");
Wizardry.proxy.setToNamedBooleanEntry(property);
bonemealGrowsCrystalFlowers = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "mobLootTableWhitelist", new String[0], "Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them."); property = config.get(GAMEPLAY_CATEGORY, "mobLootTableWhitelist", new String[0], "Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them.");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_whitelist"); property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_whitelist");
property.setRequiresMcRestart(true); property.setRequiresMcRestart(true);
@@ -700,7 +710,7 @@ public final class Settings {
fastWorldgen = property.getBoolean(); fastWorldgen = property.getBoolean();
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "towerDimensions", new int[]{0}, "List of dimension ids in which wizard towers will generate."); property = config.get(WORLDGEN_CATEGORY, "towerDimensions", new int[]{0}, "List of dimension ids in which wizard towers will generate. Remove all dimensions to disable wizard towers completely.");
property.setLanguageKey("config." + Wizardry.MODID + ".tower_dimensions"); property.setLanguageKey("config." + Wizardry.MODID + ".tower_dimensions");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
towerDimensions = property.getIntList(); towerDimensions = property.getIntList();
@@ -725,20 +735,20 @@ public final class Settings {
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "towerFiles", new String[]{Wizardry.MODID + ":wizard_tower_0", Wizardry.MODID + ":wizard_tower_1", Wizardry.MODID + ":wizard_tower_2", Wizardry.MODID + ":wizard_tower_3"}, property = config.get(WORLDGEN_CATEGORY, "towerFiles", new String[]{Wizardry.MODID + ":wizard_tower_0", Wizardry.MODID + ":wizard_tower_1", Wizardry.MODID + ":wizard_tower_2", Wizardry.MODID + ":wizard_tower_3"},
"List of structure file locations for wizard towers without loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves."); "List of structure file locations for wizard towers without loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable wizard towers, use the tower dimensions setting.");
property.setLanguageKey("config." + Wizardry.MODID + ".tower_files"); property.setLanguageKey("config." + Wizardry.MODID + ".tower_files");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
towerFiles = getResourceLocationList(property); towerFiles = getResourceLocationList(property);
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "towerWithChestFiles", new String[]{Wizardry.MODID + ":wizard_tower_chest_0", Wizardry.MODID + ":wizard_tower_chest_1", Wizardry.MODID + ":wizard_tower_chest_2", Wizardry.MODID + ":wizard_tower_chest_3"}, property = config.get(WORLDGEN_CATEGORY, "towerWithChestFiles", new String[]{Wizardry.MODID + ":wizard_tower_chest_0", Wizardry.MODID + ":wizard_tower_chest_1", Wizardry.MODID + ":wizard_tower_chest_2", Wizardry.MODID + ":wizard_tower_chest_3"},
"List of structure file locations for wizard towers with loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves."); "List of structure file locations for wizard towers with loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable wizard towers, use the tower dimensions setting.");
property.setLanguageKey("config." + Wizardry.MODID + ".tower_with_chest_files"); property.setLanguageKey("config." + Wizardry.MODID + ".tower_with_chest_files");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
towerWithChestFiles = getResourceLocationList(property); towerWithChestFiles = getResourceLocationList(property);
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "obeliskDimensions", new int[]{0, -1}, "List of dimension ids in which obelisks will generate."); property = config.get(WORLDGEN_CATEGORY, "obeliskDimensions", new int[]{0, -1}, "List of dimension ids in which obelisks will generate. Remove all dimensions to disable obelisks completely.");
property.setLanguageKey("config." + Wizardry.MODID + ".obelisk_dimensions"); property.setLanguageKey("config." + Wizardry.MODID + ".obelisk_dimensions");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
obeliskDimensions = property.getIntList(); obeliskDimensions = property.getIntList();
@@ -752,13 +762,13 @@ public final class Settings {
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "obeliskFiles", new String[]{Wizardry.MODID + ":obelisk_0", Wizardry.MODID + ":obelisk_1", Wizardry.MODID + ":obelisk_2", Wizardry.MODID + ":obelisk_3", Wizardry.MODID + ":obelisk_4"}, property = config.get(WORLDGEN_CATEGORY, "obeliskFiles", new String[]{Wizardry.MODID + ":obelisk_0", Wizardry.MODID + ":obelisk_1", Wizardry.MODID + ":obelisk_2", Wizardry.MODID + ":obelisk_3", Wizardry.MODID + ":obelisk_4"},
"List of structure file locations for obelisks. One of these files will be randomly selected each time an obelisk is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves."); "List of structure file locations for obelisks. One of these files will be randomly selected each time an obelisk is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable obelisks, use the obelisk dimensions setting.");
property.setLanguageKey("config." + Wizardry.MODID + ".obelisk_files"); property.setLanguageKey("config." + Wizardry.MODID + ".obelisk_files");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
obeliskFiles = getResourceLocationList(property); obeliskFiles = getResourceLocationList(property);
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "shrineDimensions", new int[]{0, -1}, "List of dimension ids in which shrines will generate."); property = config.get(WORLDGEN_CATEGORY, "shrineDimensions", new int[]{0, -1}, "List of dimension ids in which shrines will generate. Remove all dimensions to disable shrines completely.");
property.setLanguageKey("config." + Wizardry.MODID + ".shrine_dimensions"); property.setLanguageKey("config." + Wizardry.MODID + ".shrine_dimensions");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
shrineDimensions = property.getIntList(); shrineDimensions = property.getIntList();
@@ -772,7 +782,7 @@ public final class Settings {
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "shrineFiles", new String[]{Wizardry.MODID + ":shrine_0", Wizardry.MODID + ":shrine_1", Wizardry.MODID + ":shrine_2", Wizardry.MODID + ":shrine_3", Wizardry.MODID + ":shrine_4", Wizardry.MODID + ":shrine_5", Wizardry.MODID + ":shrine_6", Wizardry.MODID + ":shrine_7"}, property = config.get(WORLDGEN_CATEGORY, "shrineFiles", new String[]{Wizardry.MODID + ":shrine_0", Wizardry.MODID + ":shrine_1", Wizardry.MODID + ":shrine_2", Wizardry.MODID + ":shrine_3", Wizardry.MODID + ":shrine_4", Wizardry.MODID + ":shrine_5", Wizardry.MODID + ":shrine_6", Wizardry.MODID + ":shrine_7"},
"List of structure file locations for shrines. One of these files will be randomly selected each time a shrine is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves."); "List of structure file locations for shrines. One of these files will be randomly selected each time a shrine is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable shrines, use the shrine dimensions setting.");
property.setLanguageKey("config." + Wizardry.MODID + ".shrine_files"); property.setLanguageKey("config." + Wizardry.MODID + ".shrine_files");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
shrineFiles = getResourceLocationList(property); shrineFiles = getResourceLocationList(property);
@@ -31,6 +31,9 @@ import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.registry.GameRegistry; import net.minecraftforge.fml.common.registry.GameRegistry;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.File;
import java.util.Calendar;
/** /**
* <i>"Electroblob's Wizardry adds an RPG-like system of spells to Minecraft, with the aim of being as playable as * <i>"Electroblob's Wizardry adds an RPG-like system of spells to Minecraft, with the aim of being as playable as
* possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master * possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master
@@ -61,7 +64,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.2.3"; public static final String VERSION = "4.2.7";
// IDEA: Triggering of inbuilt Forge events in relevant places? // IDEA: Triggering of inbuilt Forge events in relevant places?
// IDEA: Abstract the vanilla particles behind the particle builder // IDEA: Abstract the vanilla particles behind the particle builder
@@ -82,6 +85,12 @@ public class Wizardry {
* - <b>INFO</b>: Anything that might happen during normal mod operation that the user needs to know about. */ * - <b>INFO</b>: Anything that might happen during normal mod operation that the user needs to know about. */
public static Logger logger; public static Logger logger;
/** A {@link File} object representing wizardry's config folder, {@code config/ebwizardry}). As of wizardry 4.2.4,
* this folder contains the main config file and the global spell properties folder, if used. */
public static File configDirectory;
public static boolean tisTheSeason;
// The instance of wizardry that Forge uses. // The instance of wizardry that Forge uses.
@Instance(Wizardry.MODID) @Instance(Wizardry.MODID)
public static Wizardry instance; public static Wizardry instance;
@@ -97,8 +106,13 @@ public class Wizardry {
proxy.registerResourceReloadListeners(); proxy.registerResourceReloadListeners();
configDirectory = new File(event.getModConfigurationDirectory(), Wizardry.MODID);
settings.initConfig(event); settings.initConfig(event);
Calendar calendar = Calendar.getInstance();
tisTheSeason = calendar.get(Calendar.MONTH) + 1 == 12 && calendar.get(Calendar.DAY_OF_MONTH) >= 24
&& calendar.get(Calendar.DAY_OF_MONTH) <= 26;
// Capabilities // Capabilities
WizardData.register(); WizardData.register();
DispenserCastingData.register(); DispenserCastingData.register();
@@ -33,6 +33,9 @@ import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.storage.loot.LootContext;
import net.minecraft.world.storage.loot.LootTable;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.entity.PlaySoundAtEntityEvent; import net.minecraftforge.event.entity.PlaySoundAtEntityEvent;
@@ -46,6 +49,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
/** /**
* General-purpose event handler for things that don't fit anywhere else or groups of related behaviours that are better * General-purpose event handler for things that don't fit anywhere else or groups of related behaviours that are better
@@ -366,6 +370,22 @@ public final class WizardryEventHandler {
EntityPlayer player = (EntityPlayer)event.getSource().getTrueSource(); EntityPlayer player = (EntityPlayer)event.getSource().getTrueSource();
// Compatibility with "Lycanites Mobs" -it uses custom loot drop logic which can't be hooked, given the
// number of mobs that spawn as Lycanites when this mod is active it would massively nerf the wizard drops
// if we didn't handle this
if(event.getEntity().getClass().getCanonicalName().contains("lycanitesmobs"))
{
WorldServer world = (WorldServer)event.getEntity().getEntityWorld();
LootTable table = world.getLootTableManager().getLootTableFromLocation(new ResourceLocation(Wizardry.MODID, "entities/mob_additions"));
LootContext ctx = new LootContext.Builder(world).withPlayer(player).build();
List<ItemStack> stacks = table.generateLootForPools(world.rand, ctx);
for(ItemStack stack : stacks) {
event.getEntity().entityDropItem(stack, 0f);
}
}
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){ for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){
if(stack.getItem() instanceof IManaStoringItem && !((IManaStoringItem)stack.getItem()).isManaFull(stack) if(stack.getItem() instanceof IManaStoringItem && !((IManaStoringItem)stack.getItem()).isManaFull(stack)
@@ -2,7 +2,6 @@ package electroblob.wizardry;
import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWizardHandbook; import electroblob.wizardry.item.ItemWizardHandbook;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
import electroblob.wizardry.tileentity.ContainerPortableWorkbench; import electroblob.wizardry.tileentity.ContainerPortableWorkbench;
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
@@ -48,9 +47,9 @@ public class WizardryGuiHandler implements IGuiHandler {
return new electroblob.wizardry.client.gui.handbook.GuiWizardHandbook(); return new electroblob.wizardry.client.gui.handbook.GuiWizardHandbook();
}else if(id == SPELL_BOOK){ }else if(id == SPELL_BOOK){
if(player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){ if(player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){
return new electroblob.wizardry.client.gui.GuiSpellBook(Spell.byMetadata(player.getHeldItemMainhand().getItemDamage())); return new electroblob.wizardry.client.gui.GuiSpellBook(player.getHeldItemMainhand());
}else if(player.getHeldItemOffhand().getItem() instanceof ItemSpellBook){ }else if(player.getHeldItemOffhand().getItem() instanceof ItemSpellBook){
return new electroblob.wizardry.client.gui.GuiSpellBook(Spell.byMetadata(player.getHeldItemOffhand().getItemDamage())); return new electroblob.wizardry.client.gui.GuiSpellBook(player.getHeldItemOffhand());
} }
}else if(id == PORTABLE_CRAFTING){ }else if(id == PORTABLE_CRAFTING){
return new electroblob.wizardry.client.gui.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z)); return new electroblob.wizardry.client.gui.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z));
@@ -1,5 +1,6 @@
package electroblob.wizardry.block; package electroblob.wizardry.block;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.ParticleBuilder.Type;
@@ -56,7 +57,7 @@ public class BlockCrystalFlower extends BlockBush {
@SubscribeEvent @SubscribeEvent
public static void onBonemealEvent(BonemealEvent event){ public static void onBonemealEvent(BonemealEvent event){
// Grows crystal flowers when bonemeal is used on grass // Grows crystal flowers when bonemeal is used on grass
if(event.getBlock().getBlock() == Blocks.GRASS){ if(Wizardry.settings.bonemealGrowsCrystalFlowers && event.getBlock().getBlock() == Blocks.GRASS){
BlockPos pos = event.getPos().add(event.getWorld().rand.nextInt(8) - event.getWorld().rand.nextInt(8), BlockPos pos = event.getPos().add(event.getWorld().rand.nextInt(8) - event.getWorld().rand.nextInt(8),
event.getWorld().rand.nextInt(4) - event.getWorld().rand.nextInt(4), event.getWorld().rand.nextInt(4) - event.getWorld().rand.nextInt(4),
@@ -141,7 +141,8 @@ public class BlockTransportationStone extends Block {
if(locations.isEmpty()) locations.add(here); if(locations.isEmpty()) locations.add(here);
else{ else{
locations.remove(here); // Prevents duplicates locations.remove(here); // Prevents duplicates
locations.set(Math.max(locations.size() - 1, 0), here); if(locations.isEmpty()) locations.add(here);
else locations.set(Math.max(locations.size() - 1, 0), here);
} }
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.confirm", Spells.transportation.getNameForTranslationFormatted()), true); if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.confirm", Spells.transportation.getNameForTranslationFormatted()), true);
} }
@@ -190,6 +190,11 @@ public class ClientProxy extends CommonProxy {
} }
} }
@Override
public void playBlinkEffect(EntityPlayer player){
if(Minecraft.getMinecraft().player == player) WizardryClientEventHandler.playBlinkEffect();
}
@Override @Override
public Set<String> getSpellHUDSkins(){ public Set<String> getSpellHUDSkins(){
return GuiSpellDisplay.getSkinKeys(); return GuiSpellDisplay.getSkinKeys();
@@ -23,11 +23,8 @@ import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.item.EntityArmorStand; import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.inventory.Slot; import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@@ -35,18 +32,17 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.village.MerchantRecipe; import net.minecraft.village.MerchantRecipe;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.client.event.*; import net.minecraftforge.client.event.*;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
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 org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import java.lang.reflect.Method; import java.util.ArrayList;
import java.util.List;
/** /**
* Event handler responsible for client-side only events, mostly rendering. * Event handler responsible for client-side only events, mostly rendering.
@@ -71,12 +67,6 @@ public final class WizardryClientEventHandler {
private static int blinkEffectTimer; private static int blinkEffectTimer;
/** The number of ticks the blink effect lasts for. */ /** The number of ticks the blink effect lasts for. */
private static final int BLINK_EFFECT_DURATION = 8; private static final int BLINK_EFFECT_DURATION = 8;
private static final Method unpressKey;
static {
unpressKey = ObfuscationReflectionHelper.findMethod(KeyBinding.class, "func_74505_d", void.class);
}
/** Starts the first person blink overlay effect. */ /** Starts the first person blink overlay effect. */
public static void playBlinkEffect(){ public static void playBlinkEffect(){
@@ -151,7 +141,10 @@ public final class WizardryClientEventHandler {
if(world == null) return; if(world == null) return;
for(TileEntity tileentity : world.loadedTileEntityList){ // Somehow this was throwing a CME, I have no idea why so I'm just going to cheat and copy the list
List<TileEntity> tileEntities = new ArrayList<>(world.loadedTileEntityList);
for(TileEntity tileentity : tileEntities){
if(tileentity instanceof TileEntityDispenser){ if(tileentity instanceof TileEntityDispenser){
if(DispenserCastingData.get((TileEntityDispenser)tileentity) != null){ if(DispenserCastingData.get((TileEntityDispenser)tileentity) != null){
DispenserCastingData.get((TileEntityDispenser)tileentity).update(); DispenserCastingData.get((TileEntityDispenser)tileentity).update();
@@ -1,11 +1,11 @@
package electroblob.wizardry.client.gui; package electroblob.wizardry.client.gui;
import com.google.common.collect.ImmutableMap;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils; import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.constants.Tier; import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.data.SpellGlyphData; import electroblob.wizardry.data.SpellGlyphData;
import electroblob.wizardry.data.WizardData; import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.ItemSpellBook;
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;
@@ -14,27 +14,22 @@ import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation; import net.minecraft.item.ItemStack;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import java.util.Map;
public class GuiSpellBook extends GuiScreen { public class GuiSpellBook extends GuiScreen {
private int xSize, ySize; private int xSize, ySize;
private ItemSpellBook book;
private Spell spell; private Spell spell;
private static final Map<Tier, ResourceLocation> textures = ImmutableMap.of( public GuiSpellBook(ItemStack stack){
Tier.NOVICE, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_novice.png"),
Tier.APPRENTICE, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_apprentice.png"),
Tier.ADVANCED, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_advanced.png"),
Tier.MASTER, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_master.png"));
public GuiSpellBook(Spell spell){
super(); super();
xSize = 288; xSize = 288;
ySize = 180; ySize = 180;
this.spell = spell; if(!(stack.getItem() instanceof ItemSpellBook)) throw new ClassCastException("Cannot create spell book GUI for item that does not extend ItemSpellBook!");
this.book = (ItemSpellBook)stack.getItem();
this.spell = Spell.byMetadata(stack.getItemDamage());
} }
/** /**
@@ -59,7 +54,7 @@ public class GuiSpellBook extends GuiScreen {
Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon()); Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon());
DrawingUtils.drawTexturedRect(xPos + 146, yPos + 20, 0, 0, 128, 128, 128, 128); DrawingUtils.drawTexturedRect(xPos + 146, yPos + 20, 0, 0, 128, 128, 128, 128);
Minecraft.getMinecraft().renderEngine.bindTexture(textures.get(spell.getTier())); Minecraft.getMinecraft().renderEngine.bindTexture(book.getGuiTexture(spell));
DrawingUtils.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256); DrawingUtils.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256);
super.drawScreen(par1, par2, par3); super.drawScreen(par1, par2, par3);
@@ -77,7 +72,7 @@ public class GuiSpellBook extends GuiScreen {
//this.fontRenderer.drawString("-------------------", xPos + 17, yPos + 35, 0); //this.fontRenderer.drawString("-------------------", xPos + 17, yPos + 35, 0);
if(spell.getTier() == Tier.NOVICE){ if(spell.getTier() == Tier.NOVICE){
// Basic is usually white but this doesn't show up. // Novice is usually white but this doesn't show up.
this.fontRenderer.drawString("Tier: \u00A77" + Tier.NOVICE.getDisplayName(), xPos + 17, yPos + 45, 0); this.fontRenderer.drawString("Tier: \u00A77" + Tier.NOVICE.getDisplayName(), xPos + 17, yPos + 45, 0);
}else{ }else{
this.fontRenderer.drawString("Tier: " + spell.getTier().getDisplayNameWithFormatting(), xPos + 17, yPos + 45, 0); this.fontRenderer.drawString("Tier: " + spell.getTier().getDisplayNameWithFormatting(), xPos + 17, yPos + 45, 0);
@@ -1,5 +1,6 @@
package electroblob.wizardry.client.model; package electroblob.wizardry.client.model;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockStatue; import electroblob.wizardry.block.BlockStatue;
import net.minecraft.client.model.ModelRenderer; import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@@ -13,6 +14,7 @@ public class ModelWizardArmour extends ModelArmourFixer {
ModelRenderer hatSegment4; ModelRenderer hatSegment4;
ModelRenderer hatSegment5; ModelRenderer hatSegment5;
ModelRenderer hatSegment6; ModelRenderer hatSegment6;
ModelRenderer hatBobble;
ModelRenderer robe; ModelRenderer robe;
public ModelWizardArmour(float delta){ public ModelWizardArmour(float delta){
@@ -75,6 +77,13 @@ public class ModelWizardArmour extends ModelArmourFixer {
hatSegment6.mirror = true; hatSegment6.mirror = true;
setRotation(hatSegment6, -0.5585054F, 0F, 0F); setRotation(hatSegment6, -0.5585054F, 0F, 0F);
hatBobble = new ModelRenderer(this, 52, 47);
hatBobble.addBox(0F, 0F, 0F, 3, 3, 3);
hatBobble.setRotationPoint(-1.5F, -15.1F, 4F);
hatBobble.setTextureSize(64, 64);
hatBobble.mirror = true;
setRotation(hatBobble, -0.65F, 0F, 0F);
bipedBody = new ModelRenderer(this, 16, 16); bipedBody = new ModelRenderer(this, 16, 16);
bipedBody.addBox(-4F, 0F, -2F, 8, 11, 4, delta); bipedBody.addBox(-4F, 0F, -2F, 8, 11, 4, delta);
bipedBody.setRotationPoint(0F, 0F, 0F); bipedBody.setRotationPoint(0F, 0F, 0F);
@@ -97,6 +106,7 @@ public class ModelWizardArmour extends ModelArmourFixer {
bipedHead.addChild(hatSegment4); bipedHead.addChild(hatSegment4);
bipedHead.addChild(hatSegment5); bipedHead.addChild(hatSegment5);
bipedHead.addChild(hatSegment6); bipedHead.addChild(hatSegment6);
bipedHead.addChild(hatBobble);
} }
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){ public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){
@@ -129,6 +139,8 @@ public class ModelWizardArmour extends ModelArmourFixer {
this.robe.rotateAngleX = (this.bipedLeftLeg.rotateAngleX + this.bipedRightLeg.rotateAngleX) / 2f; this.robe.rotateAngleX = (this.bipedLeftLeg.rotateAngleX + this.bipedRightLeg.rotateAngleX) / 2f;
this.robe.rotateAngleY = this.bipedBody.rotateAngleY; this.robe.rotateAngleY = this.bipedBody.rotateAngleY;
this.robe.rotateAngleZ = (this.bipedLeftLeg.rotateAngleZ + this.bipedRightLeg.rotateAngleZ) / 2f; this.robe.rotateAngleZ = (this.bipedLeftLeg.rotateAngleZ + this.bipedRightLeg.rotateAngleZ) / 2f;
this.hatBobble.showModel = Wizardry.tisTheSeason;
} }
} }
@@ -10,6 +10,7 @@ import electroblob.wizardry.item.ItemCrystal;
import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import net.minecraft.client.renderer.block.model.IBakedModel; import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ModelBakery;
import net.minecraft.client.renderer.block.model.ModelResourceLocation; import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.block.statemap.StateMap; import net.minecraft.client.renderer.block.statemap.StateMap;
import net.minecraft.creativetab.CreativeTabs; import net.minecraft.creativetab.CreativeTabs;
@@ -66,10 +67,10 @@ public final class WizardryModels {
registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal); registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal);
registerItemModel(WizardryItems.magic_wand); registerWandModel(WizardryItems.magic_wand);
registerItemModel(WizardryItems.apprentice_wand); registerWandModel(WizardryItems.apprentice_wand);
registerItemModel(WizardryItems.advanced_wand); registerWandModel(WizardryItems.advanced_wand);
registerItemModel(WizardryItems.master_wand); registerWandModel(WizardryItems.master_wand);
registerItemModel(WizardryItems.spell_book); registerItemModel(WizardryItems.spell_book);
// Wildcard registered for wizard trades. // Wildcard registered for wizard trades.
@@ -77,37 +78,37 @@ public final class WizardryModels {
registerItemModel(WizardryItems.arcane_tome); registerItemModel(WizardryItems.arcane_tome);
registerItemModel(WizardryItems.wizard_handbook); registerItemModel(WizardryItems.wizard_handbook);
registerItemModel(WizardryItems.novice_fire_wand); registerWandModel(WizardryItems.novice_fire_wand);
registerItemModel(WizardryItems.novice_ice_wand); registerWandModel(WizardryItems.novice_ice_wand);
registerItemModel(WizardryItems.novice_lightning_wand); registerWandModel(WizardryItems.novice_lightning_wand);
registerItemModel(WizardryItems.novice_necromancy_wand); registerWandModel(WizardryItems.novice_necromancy_wand);
registerItemModel(WizardryItems.novice_earth_wand); registerWandModel(WizardryItems.novice_earth_wand);
registerItemModel(WizardryItems.novice_sorcery_wand); registerWandModel(WizardryItems.novice_sorcery_wand);
registerItemModel(WizardryItems.novice_healing_wand); registerWandModel(WizardryItems.novice_healing_wand);
registerItemModel(WizardryItems.apprentice_fire_wand); registerWandModel(WizardryItems.apprentice_fire_wand);
registerItemModel(WizardryItems.apprentice_ice_wand); registerWandModel(WizardryItems.apprentice_ice_wand);
registerItemModel(WizardryItems.apprentice_lightning_wand); registerWandModel(WizardryItems.apprentice_lightning_wand);
registerItemModel(WizardryItems.apprentice_necromancy_wand); registerWandModel(WizardryItems.apprentice_necromancy_wand);
registerItemModel(WizardryItems.apprentice_earth_wand); registerWandModel(WizardryItems.apprentice_earth_wand);
registerItemModel(WizardryItems.apprentice_sorcery_wand); registerWandModel(WizardryItems.apprentice_sorcery_wand);
registerItemModel(WizardryItems.apprentice_healing_wand); registerWandModel(WizardryItems.apprentice_healing_wand);
registerItemModel(WizardryItems.advanced_fire_wand); registerWandModel(WizardryItems.advanced_fire_wand);
registerItemModel(WizardryItems.advanced_ice_wand); registerWandModel(WizardryItems.advanced_ice_wand);
registerItemModel(WizardryItems.advanced_lightning_wand); registerWandModel(WizardryItems.advanced_lightning_wand);
registerItemModel(WizardryItems.advanced_necromancy_wand); registerWandModel(WizardryItems.advanced_necromancy_wand);
registerItemModel(WizardryItems.advanced_earth_wand); registerWandModel(WizardryItems.advanced_earth_wand);
registerItemModel(WizardryItems.advanced_sorcery_wand); registerWandModel(WizardryItems.advanced_sorcery_wand);
registerItemModel(WizardryItems.advanced_healing_wand); registerWandModel(WizardryItems.advanced_healing_wand);
registerItemModel(WizardryItems.master_fire_wand); registerWandModel(WizardryItems.master_fire_wand);
registerItemModel(WizardryItems.master_ice_wand); registerWandModel(WizardryItems.master_ice_wand);
registerItemModel(WizardryItems.master_lightning_wand); registerWandModel(WizardryItems.master_lightning_wand);
registerItemModel(WizardryItems.master_necromancy_wand); registerWandModel(WizardryItems.master_necromancy_wand);
registerItemModel(WizardryItems.master_earth_wand); registerWandModel(WizardryItems.master_earth_wand);
registerItemModel(WizardryItems.master_sorcery_wand); registerWandModel(WizardryItems.master_sorcery_wand);
registerItemModel(WizardryItems.master_healing_wand); registerWandModel(WizardryItems.master_healing_wand);
registerItemModel(WizardryItems.spectral_sword); registerItemModel(WizardryItems.spectral_sword);
registerItemModel(WizardryItems.spectral_pickaxe); registerItemModel(WizardryItems.spectral_pickaxe);
@@ -294,24 +295,30 @@ public final class WizardryModels {
/** /**
* Registers an item model, using the item's registry name as the model name (this convention makes it easier to * Registers an item model, using the item's registry name as the model name (this convention makes it easier to
* keep track of everything). Variant defaults to "normal". Registers the model for metadata 0 automatically, plus * keep track of everything). Variant defaults to "normal". Registers the model for all metadata values.
* all the other metadata values that the item can take, as defined in
* {@link Item#getSubItems(CreativeTabs, NonNullList)}. The creative tab supplied
* to the aforementioned method will be whichever one the item is in.
*/ */
private static void registerItemModel(Item item){ private static void registerItemModel(Item item){
if(item.getHasSubtypes()){
NonNullList<ItemStack> items = NonNullList.create();
item.getSubItems(item.getCreativeTab(), items); // Client-only method, but we're client-side so this is OK.
for(ItemStack stack : items){
ModelLoader.setCustomModelResourceLocation(item, stack.getMetadata(),
new ModelResourceLocation(item.getRegistryName(), "inventory"));
}
}
// Changing the last parameter from null to "inventory" fixed the item/block model weirdness. No idea why! // Changing the last parameter from null to "inventory" fixed the item/block model weirdness. No idea why!
ModelLoader.setCustomModelResourceLocation(item, 0, ModelBakery.registerItemVariants(item, new ModelResourceLocation(item.getRegistryName(), "inventory"));
new ModelResourceLocation(item.getRegistryName(), "inventory")); // Assigns the model for all metadata values
ModelLoader.setCustomMeshDefinition(item, s -> new ModelResourceLocation(item.getRegistryName(), "inventory"));
}
/**
* Registers a wand model, using the item's registry name as the model name (this convention makes it easier to
* keep track of everything).
*/
private static void registerWandModel(Item item){
if(Wizardry.tisTheSeason){
// Changing the last parameter from null to "inventory" fixed the item/block model weirdness. No idea why!
ModelBakery.registerItemVariants(item, new ModelResourceLocation("ebwizardry:festive_wand", "inventory"));
// Assigns the model for all metadata values
ModelLoader.setCustomMeshDefinition(item, s -> new ModelResourceLocation("ebwizardry:festive_wand", "inventory"));
}else{
registerItemModel(item);
}
} }
/** /**
@@ -112,7 +112,7 @@ public class LayerFrost implements LayerRenderer<EntityLivingBase> {
double scaleX = 1, scaleY = 1; double scaleX = 1, scaleY = 1;
// It's more logical to use the model's texture size, but some classes don't bother setting it properly // It's more logical to use the model's texture size, but some classes don't bother setting it properly
// (e.g. ModelVillager), so to get the correct dimensions I'm getting them from the first box instead. // (e.g. ModelVillager), so to get the correct dimensions I'm getting them from the first box instead.
if(model.boxList != null && model.boxList.get(0) != null){ if(model.boxList != null && model.boxList.size() >= 1 && model.boxList.get(0) != null){
scaleX = (double)model.boxList.get(0).textureWidth / 16d; scaleX = (double)model.boxList.get(0).textureWidth / 16d;
scaleY = (double)model.boxList.get(0).textureHeight / 16d; scaleY = (double)model.boxList.get(0).textureHeight / 16d;
}else{ // Fallback to model fields; should never be needed }else{ // Fallback to model fields; should never be needed
@@ -121,11 +121,26 @@ public class LayerFrost implements LayerRenderer<EntityLivingBase> {
} }
GlStateManager.scale(scaleX, scaleY, 1); GlStateManager.scale(scaleX, scaleY, 1);
GlStateManager.matrixMode(GL11.GL_MODELVIEW); GlStateManager.matrixMode(GL11.GL_MODELVIEW);
boolean headWearHidden = false;
boolean bodyWearHidden = false;
boolean leftArmWearHidden = false;
boolean rightArmWearHidden = false;
boolean leftLegWearHidden = false;
boolean rightLegWearHidden = false;
// Hides the hat layer for bipeds // Hides the hat layer for bipeds
if(this.model instanceof ModelBiped) ((ModelBiped)this.model).bipedHeadwear.isHidden = true; if(this.model instanceof ModelBiped){
headWearHidden = ((ModelBiped)this.model).bipedHeadwear.isHidden;
((ModelBiped)this.model).bipedHeadwear.isHidden = true;
}
if(this.model instanceof ModelPlayer){ if(this.model instanceof ModelPlayer){
bodyWearHidden = ((ModelPlayer)this.model).bipedBodyWear.isHidden;
leftArmWearHidden = ((ModelPlayer)this.model).bipedLeftArmwear.isHidden;
rightArmWearHidden = ((ModelPlayer)this.model).bipedRightArmwear.isHidden;
leftLegWearHidden = ((ModelPlayer)this.model).bipedLeftLegwear.isHidden;
rightLegWearHidden = ((ModelPlayer)this.model).bipedRightLegwear.isHidden;
((ModelPlayer)this.model).bipedBodyWear.isHidden = true; ((ModelPlayer)this.model).bipedBodyWear.isHidden = true;
((ModelPlayer)this.model).bipedLeftArmwear.isHidden = true; ((ModelPlayer)this.model).bipedLeftArmwear.isHidden = true;
((ModelPlayer)this.model).bipedRightArmwear.isHidden = true; ((ModelPlayer)this.model).bipedRightArmwear.isHidden = true;
@@ -136,7 +151,15 @@ public class LayerFrost implements LayerRenderer<EntityLivingBase> {
this.model.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks); this.model.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks);
this.model.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale); this.model.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
if(this.model instanceof ModelBiped) ((ModelBiped)this.model).bipedHeadwear.isHidden = false; if(this.model instanceof ModelBiped) ((ModelBiped)this.model).bipedHeadwear.isHidden = headWearHidden;
if(this.model instanceof ModelPlayer){
((ModelPlayer)this.model).bipedBodyWear.isHidden = bodyWearHidden;
((ModelPlayer)this.model).bipedLeftArmwear.isHidden = leftArmWearHidden;
((ModelPlayer)this.model).bipedRightArmwear.isHidden = rightArmWearHidden;
((ModelPlayer)this.model).bipedLeftLegwear.isHidden = leftLegWearHidden;
((ModelPlayer)this.model).bipedRightLegwear.isHidden = rightLegWearHidden;
}
// Undoes the texture scaling // Undoes the texture scaling
GlStateManager.matrixMode(GL11.GL_TEXTURE); GlStateManager.matrixMode(GL11.GL_TEXTURE);
@@ -42,6 +42,7 @@ public class RenderArcaneLock {
GlStateManager.pushMatrix(); GlStateManager.pushMatrix();
GlStateManager.enableBlend(); GlStateManager.enableBlend();
boolean lighting = GL11.glIsEnabled(GL11.GL_LIGHTING);
GlStateManager.disableLighting(); GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
@@ -62,6 +63,8 @@ public class RenderArcaneLock {
List<TileEntity> tileentities = new ArrayList<>(world.loadedTileEntityList); List<TileEntity> tileentities = new ArrayList<>(world.loadedTileEntityList);
for(TileEntity tileentity : tileentities){ for(TileEntity tileentity : tileentities){
if(tileentity == null) continue; // What the heck VoxelMap
if(tileentity.getDistanceSq(origin.x, origin.y, origin.z) <= tileentity.getMaxRenderDistanceSquared() if(tileentity.getDistanceSq(origin.x, origin.y, origin.z) <= tileentity.getMaxRenderDistanceSquared()
&& tileentity.getTileData().hasUniqueId(ArcaneLock.NBT_KEY)){ && tileentity.getTileData().hasUniqueId(ArcaneLock.NBT_KEY)){
@@ -82,7 +85,9 @@ public class RenderArcaneLock {
GlStateManager.disableBlend(); GlStateManager.disableBlend();
GlStateManager.enableTexture2D(); GlStateManager.enableTexture2D();
GlStateManager.enableLighting(); if (lighting) {
GlStateManager.enableLighting();
}
GlStateManager.disableRescaleNormal(); GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix(); GlStateManager.popMatrix();
} }
@@ -53,9 +53,8 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
(float)blackhole.ticksExisted / 10); (float)blackhole.ticksExisted / 10);
} }
if(blackhole.ticksExisted > blackhole.lifetime - 10){ if(blackhole.ticksExisted > blackhole.lifetime - 10){
GlStateManager.scale((float)(blackhole.lifetime - blackhole.ticksExisted) / 10, float scale = Math.max(0, (float)(blackhole.lifetime - blackhole.ticksExisted) / 10);
(float)(blackhole.lifetime - blackhole.ticksExisted) / 10, GlStateManager.scale(scale, scale, scale);
(float)(blackhole.lifetime - blackhole.ticksExisted) / 10);
} }
this.bindTexture(texture); this.bindTexture(texture);
@@ -12,9 +12,9 @@ import java.util.Random;
public enum Tier { public enum Tier {
NOVICE(700, 3, 12, 0, new Style().setColor(TextFormatting.WHITE), "novice"), NOVICE(700, 3, 12, 0, new Style().setColor(TextFormatting.WHITE), "novice"),
APPRENTICE(1000, 5, 5, 4000, new Style().setColor(TextFormatting.AQUA), "apprentice"), APPRENTICE(1000, 5, 5, 2000, new Style().setColor(TextFormatting.AQUA), "apprentice"),
ADVANCED(1500, 7, 2, 6000, new Style().setColor(TextFormatting.DARK_BLUE), "advanced"), ADVANCED(1500, 7, 2, 3500, new Style().setColor(TextFormatting.DARK_BLUE), "advanced"),
MASTER(2500, 9, 1, 10000, new Style().setColor(TextFormatting.DARK_PURPLE), "master"); MASTER(2500, 9, 1, 6000, new Style().setColor(TextFormatting.DARK_PURPLE), "master");
/** Maximum mana a wand of this tier can store. */ /** Maximum mana a wand of this tier can store. */
public final int maxCharge; public final int maxCharge;
@@ -166,7 +166,10 @@ public class DispenserCastingData extends BlockCastingData<TileEntityDispenser>
// This will fire once for each dimension, but since we want dispenser-casting to work in all dimensions, // This will fire once for each dimension, but since we want dispenser-casting to work in all dimensions,
// this is correct (the loaded tile entity list will of course be different in each case. // this is correct (the loaded tile entity list will of course be different in each case.
for(TileEntity tileentity : event.world.loadedTileEntityList){ // Somehow this was throwing a CME, I have no idea why so I'm just going to cheat and copy the list
List<TileEntity> tileEntities = new ArrayList<>(event.world.loadedTileEntityList);
for(TileEntity tileentity : tileEntities){
if(tileentity instanceof TileEntityDispenser){ if(tileentity instanceof TileEntityDispenser){
if(DispenserCastingData.get((TileEntityDispenser)tileentity) != null){ if(DispenserCastingData.get((TileEntityDispenser)tileentity) != null){
DispenserCastingData.get((TileEntityDispenser)tileentity).update(); DispenserCastingData.get((TileEntityDispenser)tileentity).update();
@@ -29,9 +29,9 @@ public class EntityIceSpike extends EntityMagicConstruct {
this.setRotation(-facing.getHorizontalAngle(), WizardryUtilities.getPitch(facing)); this.setRotation(-facing.getHorizontalAngle(), WizardryUtilities.getPitch(facing));
float yaw = (-facing.getHorizontalAngle()) * (float)Math.PI/180; float yaw = (-facing.getHorizontalAngle()) * (float)Math.PI/180;
float pitch = (WizardryUtilities.getPitch(facing) - 90) * (float)Math.PI/180; float pitch = (WizardryUtilities.getPitch(facing) - 90) * (float)Math.PI/180;
Vec3d min = new Vec3d(-width/2, 0, -width/2).rotatePitch(pitch).rotateYaw(yaw); Vec3d min = this.getPositionVector().add(new Vec3d(-width/2, 0, -width/2).rotatePitch(pitch).rotateYaw(yaw));
Vec3d max = new Vec3d(width/2, height, width/2).rotatePitch(pitch).rotateYaw(yaw); Vec3d max = this.getPositionVector().add(new Vec3d(width/2, height, width/2).rotatePitch(pitch).rotateYaw(yaw));
this.setEntityBoundingBox(new AxisAlignedBB(this.getPositionVector().add(min), this.getPositionVector().add(max))); this.setEntityBoundingBox(new AxisAlignedBB(min.x, min.y, min.z, max.x, max.y, max.z));
} }
public EnumFacing getFacing(){ public EnumFacing getFacing(){
@@ -35,11 +35,17 @@ import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.EnumDifficulty; import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.event.entity.living.LivingSpawnEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
import org.apache.commons.lang3.ArrayUtils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
@Mod.EventBusSubscriber
public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntityAdditionalSpawnData { public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntityAdditionalSpawnData {
private EntityAIAttackSpell<EntityEvilWizard> spellCastingAI = new EntityAIAttackSpell<>(this, 0.5D, 14.0F, 30, 50); private EntityAIAttackSpell<EntityEvilWizard> spellCastingAI = new EntityAIAttackSpell<>(this, 0.5D, 14.0F, 30, 50);
@@ -283,16 +289,6 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
public int getMaxSpawnedInChunk(){ public int getMaxSpawnedInChunk(){
return 1; return 1;
} }
@Override
public boolean getCanSpawnHere(){
// Evil wizards can only spawn in the specified dimensions
for(int id : Wizardry.settings.mobSpawnDimensions){
if(this.dimension == id) return super.getCanSpawnHere();
}
return false;
}
@Override @Override
protected boolean canDespawn(){ protected boolean canDespawn(){
@@ -393,4 +389,13 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
textureIndex = data.readInt(); textureIndex = data.readInt();
} }
@SubscribeEvent
public static void onCheckSpawnEvent(LivingSpawnEvent.CheckSpawn event){
// We have no way of checking if it's a spawner in getCanSpawnHere() so this has to be done here instead
if(event.getEntityLiving() instanceof EntityEvilWizard && !event.isSpawner()){
if(!ArrayUtils.contains(Wizardry.settings.mobSpawnDimensions, event.getWorld().provider.getDimension()))
event.setResult(Event.Result.DENY);
}
}
} }
@@ -52,7 +52,7 @@ public class EntityIceGiant extends EntityIronGolem implements ISummonedCreature
this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F)); this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
this.tasks.addTask(7, new EntityAILookIdle(this)); this.tasks.addTask(7, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class, this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector())); 0, false, true, this.getTargetSelector()));
} }
@@ -16,7 +16,13 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.event.entity.living.LivingSpawnEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.lang3.ArrayUtils;
@Mod.EventBusSubscriber
public class EntityIceWraith extends EntityBlazeMinion { public class EntityIceWraith extends EntityBlazeMinion {
/** The version from EntityLivingBase is only used in onLivingUpdate, so it can safely be copied. */ /** The version from EntityLivingBase is only used in onLivingUpdate, so it can safely be copied. */
@@ -185,12 +191,16 @@ public class EntityIceWraith extends EntityBlazeMinion {
@Override @Override
public boolean getCanSpawnHere(){ public boolean getCanSpawnHere(){
// Only spawns in the specified dimensions return super.getCanSpawnHere() && this.isValidLightLevel();
for(int id : Wizardry.settings.mobSpawnDimensions){ }
if(this.dimension == id) return super.getCanSpawnHere() && this.isValidLightLevel();
}
return false; @SubscribeEvent
public static void onCheckSpawnEvent(LivingSpawnEvent.CheckSpawn event){
// We have no way of checking if it's a spawner in getCanSpawnHere() so this has to be done here instead
if(event.getEntityLiving() instanceof EntityLightningWraith && !event.isSpawner()){
if(!ArrayUtils.contains(Wizardry.settings.mobSpawnDimensions, event.getWorld().provider.getDimension()))
event.setResult(Event.Result.DENY);
}
} }
/** /**
@@ -13,7 +13,16 @@ import net.minecraft.init.MobEffects;
import net.minecraft.util.DamageSource; import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.event.entity.living.LivingSpawnEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.lang3.ArrayUtils;
import java.util.Arrays;
import java.util.function.IntPredicate;
@Mod.EventBusSubscriber
public class EntityLightningWraith extends EntityBlazeMinion { public class EntityLightningWraith extends EntityBlazeMinion {
public EntityLightningWraith(World world){ public EntityLightningWraith(World world){
@@ -74,14 +83,17 @@ public class EntityLightningWraith extends EntityBlazeMinion {
@Override @Override
public boolean getCanSpawnHere(){ public boolean getCanSpawnHere(){
// Only spawns in the specified dimensions, during thunderstorms return super.getCanSpawnHere() && this.isValidLightLevel();
if(!world.isThundering()) return false; }
for(int id : Wizardry.settings.mobSpawnDimensions){ @SubscribeEvent
if(this.dimension == id) return super.getCanSpawnHere() && this.isValidLightLevel(); public static void onCheckSpawnEvent(LivingSpawnEvent.CheckSpawn event){
// We have no way of checking if it's a spawner in getCanSpawnHere() so this has to be done here instead
if(event.getEntityLiving() instanceof EntityLightningWraith && !event.isSpawner()){
if(!event.getWorld().isThundering()) event.setResult(Event.Result.DENY);
if(!ArrayUtils.contains(Wizardry.settings.mobSpawnDimensions, event.getWorld().provider.getDimension()))
event.setResult(Event.Result.DENY);
} }
return false;
} }
/** /**
@@ -50,7 +50,7 @@ public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaste
this.tasks.addTask(3, new EntityAILookIdle(this)); this.tasks.addTask(3, new EntityAILookIdle(this));
// this.targetTasks.addTask(0, new EntityAIMoveTowardsTarget(this, 1, 10)); // this.targetTasks.addTask(0, new EntityAIMoveTowardsTarget(this, 1, 10));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class, this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector())); 0, false, true, this.getTargetSelector()));
this.setAIMoveSpeed((float)AISpeed); this.setAIMoveSpeed((float)AISpeed);
@@ -45,7 +45,7 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell
this.tasks.addTask(2, new EntityAIWander(this, AISpeed)); this.tasks.addTask(2, new EntityAIWander(this, AISpeed));
this.tasks.addTask(3, new EntityAILookIdle(this)); this.tasks.addTask(3, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class, this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector())); 0, false, true, this.getTargetSelector()));
this.setAIMoveSpeed((float)AISpeed); this.setAIMoveSpeed((float)AISpeed);
@@ -218,7 +218,8 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
// Copied from EntityVillager // Copied from EntityVillager
if(!this.world.isRemote && this.livingSoundTime > -this.getTalkInterval() + 20){ if(!this.world.isRemote && this.livingSoundTime > -this.getTalkInterval() + 20){
this.livingSoundTime = -this.getTalkInterval(); this.livingSoundTime = -this.getTalkInterval();
this.playSound(stack.isEmpty() ? WizardrySounds.ENTITY_WIZARD_NO : WizardrySounds.ENTITY_WIZARD_YES, this.getSoundVolume(), this.getSoundPitch()); SoundEvent yes = Wizardry.tisTheSeason ? WizardrySounds.ENTITY_WIZARD_HOHOHO : WizardrySounds.ENTITY_WIZARD_YES;
this.playSound(stack.isEmpty() ? WizardrySounds.ENTITY_WIZARD_NO : yes, this.getSoundVolume(), this.getSoundPitch());
} }
} }
@@ -256,6 +257,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
@Override @Override
protected SoundEvent getAmbientSound(){ protected SoundEvent getAmbientSound(){
if(Wizardry.tisTheSeason) return WizardrySounds.ENTITY_WIZARD_HOHOHO;
return this.isTrading() ? WizardrySounds.ENTITY_WIZARD_TRADING : WizardrySounds.ENTITY_WIZARD_AMBIENT; return this.isTrading() ? WizardrySounds.ENTITY_WIZARD_TRADING : WizardrySounds.ENTITY_WIZARD_AMBIENT;
} }
@@ -414,7 +416,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
this.spells = (List<Spell>)NBTExtras.NBTToList(nbt.getTagList("spells", NBT.TAG_INT), this.spells = (List<Spell>)NBTExtras.NBTToList(nbt.getTagList("spells", NBT.TAG_INT),
(NBTTagInt tag) -> Spell.byMetadata(tag.getInt())); (NBTTagInt tag) -> Spell.byMetadata(tag.getInt()));
NBTTagList tagList = nbt.getTagList("towerBlocks", NBT.TAG_LONG); NBTTagList tagList = nbt.getTagList("towerBlocks", NBT.TAG_COMPOUND);
if(!tagList.isEmpty()){ if(!tagList.isEmpty()){
this.towerBlocks = new HashSet<>(NBTExtras.NBTToList(tagList, NBTUtil::getPosFromTag)); this.towerBlocks = new HashSet<>(NBTExtras.NBTToList(tagList, NBTUtil::getPosFromTag));
}else{ }else{
@@ -780,6 +782,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
Tier maxTier = Tier.NOVICE; Tier maxTier = Tier.NOVICE;
List<Spell> npcSpells = Spell.getSpells(Spell.npcSpells); List<Spell> npcSpells = Spell.getSpells(Spell.npcSpells);
npcSpells.removeIf(s -> !s.applicableForItem(WizardryItems.spell_book));
for(int i = 0; i < n; i++){ for(int i = 0; i < n; i++){
@@ -221,7 +221,8 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData, IEntityOw
* possible for implementors to override this in order to do something special when selecting a target. * possible for implementors to override this in order to do something special when selecting a target.
*/ */
default Predicate<Entity> getTargetSelector(){ default Predicate<Entity> getTargetSelector(){
return entity -> getCaster() == null ? entity instanceof EntityPlayer : !entity.isInvisible() && isValidTarget(entity); return entity -> !entity.isInvisible() && (getCaster() == null ? entity instanceof EntityPlayer &&
!((EntityPlayer)entity).isCreative() : isValidTarget(entity));
} }
/** /**
@@ -1,5 +1,6 @@
package electroblob.wizardry.entity.projectile; package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.Wizardry;
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;
@@ -44,15 +45,34 @@ public class EntityMagicMissile extends EntityMagicArrow {
public void tickInAir(){ public void tickInAir(){
if(this.world.isRemote){ if(this.world.isRemote){
ParticleBuilder.create(Type.SPARKLE, rand, posX, posY, posZ, 0.03, true).clr(1, 1, 0.65f).fade(0.7f, 0, 1)
.time(20 + rand.nextInt(10)).spawn(world); if(Wizardry.tisTheSeason){
if(this.ticksExisted > 1){ // Don't spawn particles behind where it started! ParticleBuilder.create(Type.SPARKLE, rand, posX, posY, posZ, 0.03, true).clr(0.8f, 0.15f, 0.15f)
double x = posX - motionX/2; .time(20 + rand.nextInt(10)).spawn(world);
double y = posY - motionY/2;
double z = posZ - motionZ/2; ParticleBuilder.create(Type.SNOW).pos(posX, posY, posZ).spawn(world);
ParticleBuilder.create(Type.SPARKLE, rand, x, y, z, 0.03, true).clr(1, 1, 0.65f).fade(0.7f, 0, 1)
.time(20 + rand.nextInt(10)).spawn(world); if(this.ticksExisted > 1){ // Don't spawn particles behind where it started!
double x = posX - motionX / 2;
double y = posY - motionY / 2;
double z = posZ - motionZ / 2;
ParticleBuilder.create(Type.SPARKLE, rand, x, y, z, 0.03, true).clr(0.15f, 0.7f, 0.15f)
.time(20 + rand.nextInt(10)).spawn(world);
}
}else{
ParticleBuilder.create(Type.SPARKLE, rand, posX, posY, posZ, 0.03, true).clr(1, 1, 0.65f).fade(0.7f, 0, 1)
.time(20 + rand.nextInt(10)).spawn(world);
if(this.ticksExisted > 1){ // Don't spawn particles behind where it started!
double x = posX - motionX / 2;
double y = posY - motionY / 2;
double z = posZ - motionZ / 2;
ParticleBuilder.create(Type.SPARKLE, rand, x, y, z, 0.03, true).clr(1, 1, 0.65f).fade(0.7f, 0, 1)
.time(20 + rand.nextInt(10)).spawn(world);
}
} }
} }
} }
@@ -66,12 +66,11 @@ public class EntitySmokeBomb extends EntityBomb {
for(EntityLivingBase target : targets){ for(EntityLivingBase target : targets){
if(target != this.getThrower()){ if(target != this.getThrower()){
// Gives the target blindness if it is a player, mind trick otherwise (since this has the desired // Gives the target blindness, and mind trick if it's not a player (since this has the desired
// effect of preventing targeting) // effect of preventing targeting)
if(target instanceof EntityPlayer){ target.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, duration, 0));
target.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, duration, 0));
}else if(target instanceof EntityLiving){ if(target instanceof EntityLiving){
// New AI
((EntityLiving)target).setAttackTarget(null); ((EntityLiving)target).setAttackTarget(null);
target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, duration, 0)); target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, duration, 0));
} }
@@ -58,6 +58,8 @@ public interface IConjuredItem {
*/ */
default int getMaxDamageFromNBT(ItemStack stack, Spell spell){ default int getMaxDamageFromNBT(ItemStack stack, Spell spell){
if(!spell.arePropertiesInitialised()) return 600; // Failsafe, some edge-cases call this during preInit
float baseDuration = spell.getProperty(SpellConjuration.ITEM_LIFETIME).floatValue(); float baseDuration = spell.getProperty(SpellConjuration.ITEM_LIFETIME).floatValue();
if(stack.hasTagCompound() && stack.getTagCompound().hasKey(DURATION_MULTIPLIER_KEY)){ if(stack.hasTagCompound() && stack.getTagCompound().hasKey(DURATION_MULTIPLIER_KEY)){
@@ -265,7 +265,7 @@ public class ItemArtefact extends Item {
if(artefact == WizardryItems.ring_condensing){ if(artefact == WizardryItems.ring_condensing){
if(world.isRemote && player.ticksExisted % 150 == 0){ if(player.ticksExisted % 150 == 0){
for(ItemStack stack : WizardryUtilities.getHotbar(player)){ for(ItemStack stack : WizardryUtilities.getHotbar(player)){
// Needs to be both of these interfaces because this ring only recharges wands // Needs to be both of these interfaces because this ring only recharges wands
// (or more accurately, chargeable spellcasting items) // (or more accurately, chargeable spellcasting items)
@@ -276,7 +276,7 @@ public class ItemArtefact extends Item {
}else if(artefact == WizardryItems.amulet_arcane_defence){ }else if(artefact == WizardryItems.amulet_arcane_defence){
if(world.isRemote && player.ticksExisted % 300 == 0){ if(player.ticksExisted % 300 == 0){
for(ItemStack stack : player.getArmorInventoryList()){ for(ItemStack stack : player.getArmorInventoryList()){
// IManaStoringItem is sufficient, since anything in the armour slots is probably armour // IManaStoringItem is sufficient, since anything in the armour slots is probably armour
if(stack.getItem() instanceof IManaStoringItem) if(stack.getItem() instanceof IManaStoringItem)
@@ -601,7 +601,7 @@ public class ItemArtefact extends Item {
}else if(artefact == WizardryItems.amulet_transience){ }else if(artefact == WizardryItems.amulet_transience){
if(player.getHealth() <= 2 && player.world.rand.nextFloat() < 0.25f){ if(player.getHealth() <= 6 && player.world.rand.nextFloat() < 0.25f){
player.addPotionEffect(new PotionEffect(WizardryPotions.transience, 300)); player.addPotionEffect(new PotionEffect(WizardryPotions.transience, 300));
player.addPotionEffect(new PotionEffect(MobEffects.INVISIBILITY, 300, 0, false, false)); player.addPotionEffect(new PotionEffect(MobEffects.INVISIBILITY, 300, 0, false, false));
} }
@@ -639,6 +639,7 @@ public class ItemArtefact extends Item {
WizardryUtilities.getEntitiesWithinRadius(3, player.posX, player.posY, player.posZ, world).stream() WizardryUtilities.getEntitiesWithinRadius(3, player.posX, player.posY, player.posZ, world).stream()
.filter(WizardryUtilities::isLiving) .filter(WizardryUtilities::isLiving)
.filter(e -> e != player)
.min(Comparator.comparingDouble(player::getDistanceSq)) .min(Comparator.comparingDouble(player::getDistanceSq))
.ifPresent(target -> { .ifPresent(target -> {
@@ -1,5 +1,6 @@
package electroblob.wizardry.item; package electroblob.wizardry.item;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Constants; import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.data.WizardData; import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
@@ -11,11 +12,13 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Slot; import net.minecraft.inventory.Slot;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
public class ItemBlankScroll extends Item implements IWorkbenchItem { public class ItemBlankScroll extends Item implements IWorkbenchItem {
public ItemBlankScroll(){ public ItemBlankScroll(){
this.setCreativeTab(WizardryTabs.WIZARDRY); this.setCreativeTab(WizardryTabs.WIZARDRY);
this.addPropertyOverride(new ResourceLocation("festive"), (s, w, e) -> Wizardry.tisTheSeason ? 1 : 0);
} }
@Override @Override
@@ -45,10 +48,14 @@ public class ItemBlankScroll extends Item implements IWorkbenchItem {
int cost = spell.getCost() * centre.getStack().getCount(); int cost = spell.getCost() * centre.getStack().getCount();
// Continuous spell scrolls require enough mana to cast them for the duration defined in ItemScroll. // Continuous spell scrolls require enough mana to cast them for the duration defined in ItemScroll.
if(spell.isContinuous) cost *= ItemScroll.CASTING_TIME / 20; if(spell.isContinuous) cost *= ItemScroll.CASTING_TIME / 20;
int manaPerItem = Constants.MANA_PER_CRYSTAL;
if(crystals.getStack().getItem() == WizardryItems.crystal_shard) manaPerItem = Constants.MANA_PER_SHARD;
if(crystals.getStack().getItem() == WizardryItems.grand_crystal) manaPerItem = Constants.GRAND_CRYSTAL_MANA;
if(crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL > cost){ if(crystals.getStack().getCount() * manaPerItem > cost){
// Rounds up to the nearest whole crystal // Rounds up to the nearest whole crystal
crystals.decrStackSize(cost / Constants.MANA_PER_CRYSTAL + 1); crystals.decrStackSize(cost / manaPerItem + 1);
centre.putStack(new ItemStack(WizardryItems.scroll, centre.getStack().getCount(), spell.metadata())); centre.putStack(new ItemStack(WizardryItems.scroll, centre.getStack().getCount(), spell.metadata()));
return true; return true;
} }
@@ -3,6 +3,7 @@ package electroblob.wizardry.item;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.Spell;
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.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
@@ -81,6 +82,12 @@ public class ItemFrostAxe extends ItemAxe implements IConjuredItem {
stack.setItemDamage(damage + 1); stack.setItemDamage(damage + 1);
} }
@Override
public Multimap<String, AttributeModifier> getItemAttributeModifiers(EntityEquipmentSlot equipmentSlot){
attackDamage = Spells.frost_axe.getProperty(Spell.DAMAGE).floatValue();
return super.getItemAttributeModifiers(equipmentSlot);
}
@Override @Override
public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase wielder){ public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase wielder){
if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) if(!MagicDamage.isEntityImmune(DamageType.FROST, target))
@@ -14,10 +14,7 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult; import net.minecraft.util.*;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
@@ -37,6 +34,7 @@ public class ItemScroll extends Item implements ISpellCastingItem {
setHasSubtypes(true); setHasSubtypes(true);
setMaxStackSize(16); setMaxStackSize(16);
setCreativeTab(WizardryTabs.SPELLS); setCreativeTab(WizardryTabs.SPELLS);
this.addPropertyOverride(new ResourceLocation("festive"), (s, w, e) -> Wizardry.tisTheSeason ? 1 : 0);
} }
@Override @Override
@@ -51,13 +49,14 @@ public class ItemScroll extends Item implements ISpellCastingItem {
@Override @Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> list){ public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> list){
if(tab == WizardryTabs.SPELLS){ if(tab == WizardryTabs.SPELLS){
// In this particular case, getTotalSpellCount() is a more efficient way of doing this since the spell instance
// is not required, only the metadata. List<Spell> spells = Spell.getSpells(Spell.allSpells);
for(int i = 0; i < Spell.getTotalSpellCount(); i++){ spells.removeIf(s -> !s.applicableForItem(this));
// i+1 is used so that the metadata ties up with the metadata() method. In other words, the none spell has metadata
// 0 and since this is not used as a spell book the metadata starts at 1. for(Spell spell : spells){
list.add(new ItemStack(this, 1, i + 1)); list.add(new ItemStack(this, 1, spell.metadata()));
} }
} }
} }
@@ -1,7 +1,9 @@
package electroblob.wizardry.item; package electroblob.wizardry.item;
import com.google.common.collect.ImmutableMap;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryGuiHandler; import electroblob.wizardry.WizardryGuiHandler;
import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.data.SpellGlyphData; import electroblob.wizardry.data.SpellGlyphData;
import electroblob.wizardry.registry.WizardryTabs; import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
@@ -9,35 +11,41 @@ import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult; import net.minecraft.util.*;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.oredict.OreDictionary;
import java.util.List; import java.util.List;
import java.util.Map;
public class ItemSpellBook extends Item { public class ItemSpellBook extends Item {
private static final Map<Tier, ResourceLocation> guiTextures = ImmutableMap.of(
Tier.NOVICE, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_novice.png"),
Tier.APPRENTICE, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_apprentice.png"),
Tier.ADVANCED, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_advanced.png"),
Tier.MASTER, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_master.png"));
public ItemSpellBook(){ public ItemSpellBook(){
super(); super();
setHasSubtypes(true); setHasSubtypes(true);
setMaxStackSize(16); setMaxStackSize(16);
setCreativeTab(WizardryTabs.SPELLS); setCreativeTab(WizardryTabs.SPELLS);
this.addPropertyOverride(new ResourceLocation("festive"), (s, w, e) -> Wizardry.tisTheSeason ? 1 : 0);
} }
@Override @Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> list){ public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> list){
if(tab == WizardryTabs.SPELLS){ if(tab == WizardryTabs.SPELLS){
// In this particular case, getTotalSpellCount() is a more efficient way of doing this since the spell instance
// is not required, only the metadata. List<Spell> spells = Spell.getSpells(Spell.allSpells);
for(int i = 0; i < Spell.getTotalSpellCount(); i++){ spells.removeIf(s -> !s.applicableForItem(this));
// i+1 is used so that the metadata ties up with the metadata() method. In other words, the none spell has metadata
// 0 and since this is not used as a spell book the metadata starts at 1. for(Spell spell : spells){
list.add(new ItemStack(this, 1, i + 1)); list.add(new ItemStack(this, 1, spell.metadata()));
} }
} }
} }
@@ -79,4 +87,12 @@ public class ItemSpellBook extends Item {
return Wizardry.proxy.getFontRenderer(stack); return Wizardry.proxy.getFontRenderer(stack);
} }
/**
* Returns the GUI texture to be used when this spell book is opened.
* @param spell The spell for the book being opened.
*/
public ResourceLocation getGuiTexture(Spell spell){
return guiTextures.get(spell.getTier());
}
} }
@@ -27,10 +27,7 @@ import net.minecraft.inventory.Slot;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult; import net.minecraft.util.*;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@@ -400,7 +397,7 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(Source.WAND, spell, caster, modifiers, castingTick))) return false; if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(Source.WAND, spell, caster, modifiers, castingTick))) return false;
} }
int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST)); int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST) + 0.1f); // Weird floaty rounding
// As of wizardry 4.2 mana cost is only divided over two intervals each second // As of wizardry 4.2 mana cost is only divided over two intervals each second
if(spell.isContinuous) cost = getDistributedCost(cost, castingTick); if(spell.isContinuous) cost = getDistributedCost(cost, castingTick);
@@ -436,7 +433,7 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
caster.setActiveHand(hand); caster.setActiveHand(hand);
// Mana cost // Mana cost
int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST)); int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST) + 0.1f); // Weird floaty rounding
// As of wizardry 4.2 mana cost is only divided over two intervals each second // As of wizardry 4.2 mana cost is only divided over two intervals each second
if(spell.isContinuous) cost = getDistributedCost(cost, castingTick); if(spell.isContinuous) cost = getDistributedCost(cost, castingTick);
@@ -497,7 +494,7 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
int castingTick = stack.getMaxItemUseDuration() - timeLeft; // Might as well include this int castingTick = stack.getMaxItemUseDuration() - timeLeft; // Might as well include this
int cost = getDistributedCost((int)(spell.getCost() * modifiers.get(SpellModifiers.COST)), castingTick); int cost = getDistributedCost((int)(spell.getCost() * modifiers.get(SpellModifiers.COST) + 0.1f), castingTick);
// Still need to check there's enough mana or the spell will finish twice, since running out of mana is // Still need to check there's enough mana or the spell will finish twice, since running out of mana is
// handled separately. // handled separately.
@@ -57,12 +57,12 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
} }
/** Should only be used by vanilla's armour damage calculations; use {@link ItemWizardArmour#setMana(ItemStack, int)} /** Should only be used by vanilla's armour damage calculations; use {@link ItemWizardArmour#setMana(ItemStack, int)}
* to modify wand mana from elsewhere. */ * to modify armour mana from elsewhere. */
@Override @Override
public void setDamage(ItemStack stack, int damage){ public void setDamage(ItemStack stack, int damage){
// Overridden to stop repair things from 'repairing' the mana in wizard armour // Overridden to stop repair things from 'repairing' the mana in wizard armour
// This being armour, it's much easier to let its damage increase normally, but block it from being decreased // This being armour, it's much easier to let its damage increase normally, but block it from being decreased
if(stack.getItemDamage() < damage) super.setDamage(stack, damage); if(stack.getItemDamage() < damage) super.setDamage(stack, Math.min(damage, stack.getMaxDamage()));
} }
@Override @Override
@@ -178,9 +178,14 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
String s = "wizard_armour"; String s = "wizard_armour";
if(this.element != null) s = s + "_" + this.element.getName(); if(Wizardry.tisTheSeason){
s = s + "_festive";
}else{
if(this.element != null) s = s + "_" + this.element.getName();
if(stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary")) s = "legendary_" + s;
}
if(slot == EntityEquipmentSlot.LEGS) s = s + "_legs"; if(slot == EntityEquipmentSlot.LEGS) s = s + "_legs";
if(stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary")) s = "legendary_" + s;
return "ebwizardry:textures/armour/" + s + ".png"; return "ebwizardry:textures/armour/" + s + ".png";
} }
@@ -108,10 +108,14 @@ public class RandomSpell extends LootFunction {
// element = null; // element = null;
} }
SpellProperties.Context spellContext = context.getLootedEntity() == null ? SpellProperties.Context.TREASURE
: SpellProperties.Context.LOOTING;
// Here's a thought: does randomly selecting the element beforehand (as opposed to leaving it null and letting // Here's a thought: does randomly selecting the element beforehand (as opposed to leaving it null and letting
// the spell randomiser use any element) change the overall outcome at all? // the spell randomiser use any element) change the overall outcome at all?
List<Spell> spellsList = Spell.getSpells(new Spell.TierElementFilter(tier, element, SpellProperties.Context.TREASURE)); List<Spell> spellsList = Spell.getSpells(new Spell.TierElementFilter(tier, element, spellContext));
spellsList.removeIf(s -> !s.applicableForItem(stack.getItem()));
if(stack.getItem() instanceof ItemScroll) spellsList.removeIf(s -> !s.isEnabled(SpellProperties.Context.SCROLL)); if(stack.getItem() instanceof ItemScroll) spellsList.removeIf(s -> !s.isEnabled(SpellProperties.Context.SCROLL));
if(stack.getItem() instanceof ItemSpellBook) spellsList.removeIf(s -> !s.isEnabled(SpellProperties.Context.BOOK)); if(stack.getItem() instanceof ItemSpellBook) spellsList.removeIf(s -> !s.isEnabled(SpellProperties.Context.BOOK));
@@ -5,6 +5,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializationContext;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.living.ISpellCaster; import electroblob.wizardry.entity.living.ISpellCaster;
import electroblob.wizardry.item.ItemScroll;
import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
@@ -33,13 +34,18 @@ public class WizardSpell extends LootFunction {
@Override @Override
public ItemStack apply(ItemStack stack, Random random, LootContext context){ public ItemStack apply(ItemStack stack, Random random, LootContext context){
if(!(stack.getItem() instanceof ItemSpellBook)) Wizardry.logger if(!(stack.getItem() instanceof ItemSpellBook) && !(stack.getItem() instanceof ItemScroll)) Wizardry.logger
.warn("Applying the wizard_spell loot function to an item that isn't a spell book or scroll."); .warn("Applying the wizard_spell loot function to an item that isn't a spell book or scroll.");
if(context.getLootedEntity() instanceof ISpellCaster){ if(context.getLootedEntity() instanceof ISpellCaster){
List<Spell> spells = ((ISpellCaster)context.getLootedEntity()).getSpells(); List<Spell> spells = ((ISpellCaster)context.getLootedEntity()).getSpells();
spells.remove(Spells.magic_missile); // Can't drop magic missile spells.remove(Spells.magic_missile); // Can't drop magic missile
stack.setItemDamage(spells.get(random.nextInt(spells.size())).metadata()); spells.removeIf(s -> !s.applicableForItem(stack.getItem()));
if(spells.isEmpty()){
Wizardry.logger.warn("Tried to apply the wizard_spell loot function to an item, but none of the looted entity's spells were applicable for that item. This is probably a bug!");
}else{
stack.setItemDamage(spells.get(random.nextInt(spells.size())).metadata());
}
}else{ }else{
Wizardry.logger.warn("Applying the wizard_spell loot function to an entity that isn't a spell caster."); Wizardry.logger.warn("Applying the wizard_spell loot function to an entity that isn't a spell caster.");
} }
@@ -162,7 +162,7 @@ public abstract class Forfeit {
event.setCanceled(true); event.setCanceled(true);
Forfeit forfeit = getRandomForfeit(event.getWorld().rand, event.getSpell().getTier(), event.getSpell().getElement()); Forfeit forfeit = getRandomForfeit(data.synchronisedRandom, event.getSpell().getTier(), event.getSpell().getElement());
if(forfeit == null){ // Should never happen, but just in case... if(forfeit == null){ // Should never happen, but just in case...
if(!event.getWorld().isRemote) player.sendMessage(new TextComponentTranslation("forfeit.ebwizardry:do_nothing")); if(!event.getWorld().isRemote) player.sendMessage(new TextComponentTranslation("forfeit.ebwizardry:do_nothing"));
@@ -243,7 +243,12 @@ public final class Spells {
IForgeRegistry<Spell> registry = event.getRegistry(); IForgeRegistry<Spell> registry = event.getRegistry();
registry.register(new None()); registry.register(new None());
registry.register(new SpellArrow<>("magic_missile", EntityMagicMissile::new).addProperties(Spell.DAMAGE).soundValues(1, 1.4f, 0.4f)); registry.register(new SpellArrow<EntityMagicMissile>("magic_missile", EntityMagicMissile::new){
@Override
protected String getTranslationKey(){
return Wizardry.tisTheSeason ? super.getTranslationKey() + "_festive" : super.getTranslationKey();
}
}.addProperties(Spell.DAMAGE).soundValues(1, 1.4f, 0.4f));
registry.register(new Ignite()); registry.register(new Ignite());
registry.register(new Freeze()); registry.register(new Freeze());
registry.register(new Snowball()); registry.register(new Snowball());
@@ -88,6 +88,7 @@ public final class WizardrySounds {
public static final SoundEvent ENTITY_WIZARD_TRADING = createSound("entity.wizard.trading"); public static final SoundEvent ENTITY_WIZARD_TRADING = createSound("entity.wizard.trading");
public static final SoundEvent ENTITY_WIZARD_HURT = createSound("entity.wizard.hurt"); public static final SoundEvent ENTITY_WIZARD_HURT = createSound("entity.wizard.hurt");
public static final SoundEvent ENTITY_WIZARD_DEATH = createSound("entity.wizard.death"); public static final SoundEvent ENTITY_WIZARD_DEATH = createSound("entity.wizard.death");
public static final SoundEvent ENTITY_WIZARD_HOHOHO = createSound("entity.wizard.hohoho");
public static final SoundEvent ENTITY_DARKNESS_ORB_HIT = createSound("entity.darkness_orb.hit"); public static final SoundEvent ENTITY_DARKNESS_ORB_HIT = createSound("entity.darkness_orb.hit");
public static final SoundEvent ENTITY_DART_HIT = createSound("entity.dart.hit"); public static final SoundEvent ENTITY_DART_HIT = createSound("entity.dart.hit");
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.event.SpellCastEvent; import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.registry.WizardryPotions;
@@ -18,12 +19,21 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraftforge.event.entity.living.LivingEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.lang.reflect.Field;
@Mod.EventBusSubscriber @Mod.EventBusSubscriber
public class ArcaneJammer extends SpellRay { public class ArcaneJammer extends SpellRay {
private static final Field spellTicks;
static { // Yay more reflection
spellTicks = ObfuscationReflectionHelper.findField(EntitySpellcasterIllager.class, "field_193087_b");
}
public ArcaneJammer(){ public ArcaneJammer(){
super("arcane_jammer", false, EnumAction.NONE); super("arcane_jammer", false, EnumAction.NONE);
this.soundValues(0.7f, 1, 0.4f); this.soundValues(0.7f, 1, 0.4f);
@@ -68,9 +78,17 @@ public class ArcaneJammer extends SpellRay {
@SubscribeEvent @SubscribeEvent
public static void onLivingUpdateEvent(LivingEvent.LivingUpdateEvent event){ public static void onLivingUpdateEvent(LivingEvent.LivingUpdateEvent event){
if(event.getEntity() instanceof EntitySpellcasterIllager if(event.getEntity() instanceof EntitySpellcasterIllager
&& event.getEntityLiving().isPotionActive(WizardryPotions.arcane_jammer)){ && event.getEntityLiving().isPotionActive(WizardryPotions.arcane_jammer)){
((EntitySpellcasterIllager)event.getEntity()).setSpellType(EntitySpellcasterIllager.SpellType.NONE); ((EntitySpellcasterIllager)event.getEntity()).setSpellType(EntitySpellcasterIllager.SpellType.NONE);
try{
spellTicks.set(event.getEntity(), 10);
}catch(IllegalAccessException e){
Wizardry.logger.error("Error setting evoker spell timer:", e);
}
} }
} }
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.ParticleBuilder.Type;
@@ -7,6 +8,7 @@ import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.EnumParticleTypes;
@@ -78,9 +80,7 @@ public class Banish extends SpellRay {
world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5);
} }
// Can't be bothered to route this through the proxies! if(entity instanceof EntityPlayer) Wizardry.proxy.playBlinkEffect((EntityPlayer)entity);
if(entity == net.minecraft.client.Minecraft.getMinecraft().player)
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect();
} }
if(y != null){ if(y != null){
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.util.RayTracer; import electroblob.wizardry.util.RayTracer;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
@@ -41,9 +42,8 @@ public class Blink extends Spell {
world.spawnParticle(EnumParticleTypes.PORTAL, dx, dy, dz, world.rand.nextDouble() - 0.5, world.spawnParticle(EnumParticleTypes.PORTAL, dx, dy, dz, world.rand.nextDouble() - 0.5,
world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5);
} }
// Can't be bothered to route this through the proxies! Wizardry.proxy.playBlinkEffect(caster);
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect();
} }
if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Constants; import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.event.SpellCastEvent; import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
@@ -66,6 +67,11 @@ public class EmpoweringPresence extends Spell {
return true; return true;
} }
@Override
protected String getTranslationKey(){
return Wizardry.tisTheSeason ? super.getTranslationKey() + "_festive" : super.getTranslationKey();
}
@SubscribeEvent(priority = EventPriority.LOW) // Doesn't really matter but there's no point processing it if casting is blocked @SubscribeEvent(priority = EventPriority.LOW) // Doesn't really matter but there's no point processing it if casting is blocked
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){ public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
// Empowerment stacks extra potency on top of the existing potency. // Empowerment stacks extra potency on top of the existing potency.
@@ -78,4 +84,5 @@ public class EmpoweringPresence extends Spell {
event.getModifiers().get(SpellModifiers.POTENCY) * potency, true); event.getModifiers().get(SpellModifiers.POTENCY) * potency, true);
} }
} }
} }
@@ -11,6 +11,7 @@ public class FrostAxe extends SpellConjuration {
public FrostAxe(){ public FrostAxe(){
super("frost_axe", WizardryItems.frost_axe); super("frost_axe", WizardryItems.frost_axe);
addProperties(DAMAGE);
} }
@Override @Override
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.util.AllyDesignationSystem; import electroblob.wizardry.util.AllyDesignationSystem;
import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder;
@@ -62,4 +63,9 @@ public class InvigoratingPresence extends Spell {
return true; return true;
} }
@Override
protected String getTranslationKey(){
return Wizardry.tisTheSeason ? super.getTranslationKey() + "_festive" : super.getTranslationKey();
}
} }
@@ -43,8 +43,7 @@ public class PhaseStep extends Spell {
world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5);
} }
// Can't be bothered to route this through the proxies! Wizardry.proxy.playBlinkEffect(caster);
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect();
} }
if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){
@@ -10,6 +10,7 @@ import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.packet.PacketSpellProperties; import electroblob.wizardry.packet.PacketSpellProperties;
import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.SpellProperties; import electroblob.wizardry.util.SpellProperties;
@@ -18,6 +19,7 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.item.Item;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
@@ -27,6 +29,10 @@ import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.network.FMLNetworkEvent;
import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.registries.ForgeRegistry; import net.minecraftforge.registries.ForgeRegistry;
@@ -82,6 +88,7 @@ import java.util.stream.Collectors;
* @see ItemScroll ItemScroll * @see ItemScroll ItemScroll
* @see Spells * @see Spells
*/ */
@Mod.EventBusSubscriber
public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements Comparable<Spell> { public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements Comparable<Spell> {
// Spell checklist: // Spell checklist:
@@ -115,6 +122,8 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
private final String unlocalisedName; private final String unlocalisedName;
/** This spell's associated SpellProperties object. */ /** This spell's associated SpellProperties object. */
private SpellProperties properties; private SpellProperties properties;
/** A reference to the global spell properties for this spell, so they are only loaded once. */
private SpellProperties globalProperties;
/** Used in initialisation. */ /** Used in initialisation. */
private Set<String> propertyKeys = new HashSet<>(); private Set<String> propertyKeys = new HashSet<>();
@@ -248,6 +257,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
if(!arePropertiesInitialised()){ if(!arePropertiesInitialised()){
this.properties = properties; this.properties = properties;
if(this.globalProperties == null) this.globalProperties = properties;
}else{ }else{
Wizardry.logger.info("A mod attempted to set a spell's properties, but they were already initialised."); Wizardry.logger.info("A mod attempted to set a spell's properties, but they were already initialised.");
} }
@@ -264,9 +274,14 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
.map(s -> s.properties).toArray(SpellProperties[]::new))); .map(s -> s.properties).toArray(SpellProperties[]::new)));
}else{ }else{
// On the client side, wipe the spell properties so the new ones can be set // On the client side, wipe the spell properties so the new ones can be set
for(Spell spell : registry){ // TESTME: Can we guarantee this happens before the packet arrives?
spell.properties = null; // TESTME: Can we guarantee this happens before the packet arrives? clearProperties();
} }
}
private static void clearProperties(){
for(Spell spell : registry){
spell.properties = null;
} }
} }
@@ -559,6 +574,13 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
this.enabled = isEnabled; this.enabled = isEnabled;
} }
/** Returns true if the given item has a variant for this spell. By default, returns true if the given item is
* either {@link WizardryItems#spell_book} or {@link WizardryItems#scroll}. Override to give the spell a special
* type of book or scroll. */
public boolean applicableForItem(Item item){
return item == WizardryItems.spell_book || item == WizardryItems.scroll;
}
/** /**
* Returns the unlocalised name of the spell, without any prefixes or suffixes, e.g. "flame_ray". <b>This should * Returns the unlocalised name of the spell, without any prefixes or suffixes, e.g. "flame_ray". <b>This should
* only be used for translation purposes.</b> * only be used for translation purposes.</b>
@@ -575,13 +597,23 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
* translating it. If, for whatever reason, you need to supply an ITextComponent but don't want it translated * translating it. If, for whatever reason, you need to supply an ITextComponent but don't want it translated
* (perhaps a name?), you can use TextComponentString, which will simply keep the raw string it is given. */ * (perhaps a name?), you can use TextComponentString, which will simply keep the raw string it is given. */
/** Returns the translation key for this spell. */
protected String getTranslationKey(){
return "spell." + unlocalisedName;
}
/** Returns the translation key for this spell's description. */
protected String getDescriptionTranslationKey(){
return "spell." + unlocalisedName + ".desc";
}
/** /**
* Returns the translated display name of the spell, without formatting (i.e. not coloured). <b>Client-side * Returns the translated display name of the spell, without formatting (i.e. not coloured). <b>Client-side
* only!</b> On the server side, use {@link TextComponentTranslation} (see {@link Spell#getNameForTranslation()}). * only!</b> On the server side, use {@link TextComponentTranslation} (see {@link Spell#getNameForTranslation()}).
*/ */
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
public String getDisplayName(){ public String getDisplayName(){
return net.minecraft.client.resources.I18n.format("spell." + unlocalisedName); return net.minecraft.client.resources.I18n.format(getTranslationKey());
} }
/** /**
@@ -589,7 +621,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
* formatting (i.e. not coloured). * formatting (i.e. not coloured).
*/ */
public ITextComponent getNameForTranslation(){ public ITextComponent getNameForTranslation(){
return new TextComponentTranslation("spell." + unlocalisedName); return new TextComponentTranslation(getTranslationKey());
} }
/** /**
@@ -598,7 +630,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
*/ */
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
public String getDisplayNameWithFormatting(){ public String getDisplayNameWithFormatting(){
return this.getElement().getFormattingCode() + net.minecraft.client.resources.I18n.format("spell." + unlocalisedName); return this.getElement().getFormattingCode() + net.minecraft.client.resources.I18n.format(getTranslationKey());
} }
/** /**
@@ -606,7 +638,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
* formatting (i.e. coloured). * formatting (i.e. coloured).
*/ */
public ITextComponent getNameForTranslationFormatted(){ public ITextComponent getNameForTranslationFormatted(){
return new TextComponentTranslation("spell." + unlocalisedName).setStyle(this.getElement().getColour()); return new TextComponentTranslation(getTranslationKey()).setStyle(this.getElement().getColour());
} }
/** /**
@@ -615,7 +647,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
*/ */
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
public String getDescription(){ public String getDescription(){
return net.minecraft.client.resources.I18n.format("spell." + unlocalisedName + ".desc"); return net.minecraft.client.resources.I18n.format(getDescriptionTranslationKey());
} }
// ============================================ Sound methods ============================================== // ============================================ Sound methods ==============================================
@@ -862,4 +894,32 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
&& (this.element == null || spell.getElement() == this.element); && (this.element == null || spell.getElement() == this.element);
} }
} }
// ============================================ Event handlers ==============================================
// Not ideal but it solves the reloading of spell properties without breaking encapsulation
@SubscribeEvent
public static void onWorldLoadEvent(WorldEvent.Load event){
if(!event.getWorld().isRemote){
if(event.getWorld().provider.getDimension() != 0) return; // Only do it once per save file
clearProperties();
SpellProperties.loadWorldSpecificSpellProperties(event.getWorld());
for(Spell spell : Spell.registry){
if(!spell.arePropertiesInitialised()) spell.setProperties(spell.globalProperties);
}
}
}
@SubscribeEvent
public static void onClientDisconnectEvent(FMLNetworkEvent.ClientDisconnectionFromServerEvent event){
// Why does the world UNLOAD event happen during world LOADING? How does that even work?!
clearProperties();
for(Spell spell : Spell.registry){
// If someone wants to access them from the menu, they'll get the global ones (not sure why you'd want to)
// No need to sync here since the server is about to shut down anyway
spell.setProperties(spell.globalProperties);
}
}
} }
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.ParticleBuilder.Type;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
@@ -9,7 +10,11 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
public class SummonSnowGolem extends Spell { public class SummonSnowGolem extends Spell {
@@ -45,4 +50,8 @@ public class SummonSnowGolem extends Spell {
return true; return true;
} }
@Override
protected String getTranslationKey(){
return Wizardry.tisTheSeason ? super.getTranslationKey() + "_festive" : super.getTranslationKey();
}
} }
@@ -13,13 +13,16 @@ import electroblob.wizardry.spell.Spell;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.util.JsonUtils; import net.minecraft.util.JsonUtils;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.crafting.CraftingHelper; import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.ModContainer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.*; import java.util.*;
@@ -40,9 +43,6 @@ import java.util.stream.Collectors;
* @author Electroblob * @author Electroblob
* @since Wizardry 4.2 * @since Wizardry 4.2
*/ */
// There is not a particular semantic reason to separate this from the Spell class itself. However, doing so means that
// everything related to the JSON spell system is kept in one place and doesn't clutter the (already long) Spell class.
// Additionally, it allows SpellProperties objects to be passed around during loading and syncing.
public final class SpellProperties { public final class SpellProperties {
private static final Gson gson = new Gson(); private static final Gson gson = new Gson();
@@ -245,32 +245,57 @@ public final class SpellProperties {
// things don't work as expected then that may be why - pretty sure it's fine though since the properties get // things don't work as expected then that may be why - pretty sure it's fine though since the properties get
// wiped client-side on each login anyway. // wiped client-side on each login anyway.
public static void init(){ public static void init(){
// Collecting to a set should give us one of each mod ID // Collecting to a set should give us one of each mod ID
Set<String> modIDs = Spell.getSpells(Spell.allSpells).stream().map(s -> s.getRegistryName().getNamespace()).collect(Collectors.toSet()); Set<String> modIDs = Spell.getSpells(Spell.allSpells).stream().map(s -> s.getRegistryName().getNamespace()).collect(Collectors.toSet());
boolean flag = true; boolean flag = loadConfigSpellProperties();
for(String modID : modIDs){ for(String modID : modIDs){
flag &= loadSpellProperties(modID); // Don't short-circuit, or mods later on won't get loaded! flag &= loadBuiltInSpellProperties(modID); // Don't short-circuit, or mods later on won't get loaded!
} }
if(!flag) Wizardry.logger.warn("Some spell property files did not load correctly; this will likely cause problems later!"); if(!flag) Wizardry.logger.warn("Some spell property files did not load correctly; this will likely cause problems later!");
} }
// Sooooooo I just realised that resource packs - you know, that famously client-side thing - can now define // There are now three 'layers' of spell properties - in order of priority, these are:
// stuff that should be specified by the server. No wonder it all got moved to data packs in 1.13... // 1. World-specific properties, stored in saves/[world]/data/spells
// Anyway, for the time being we're in 1.12 so we're gonna have to do this instead. // 2. Global overrides, stored in config/ebwizardry/spells
// 3. Built-in properties, stored in mods/[mod jar]/assets/spells
// There's a method for loading each of these below, because that makes sense to me!
public static void loadWorldSpecificSpellProperties(World world){
Wizardry.logger.info("Loading custom spell properties for world {}", world.getWorldInfo().getWorldName());
File spellJSONDir = new File(new File(world.getSaveHandler().getWorldDirectory(), "data"), "spells");
if(spellJSONDir.mkdirs()) return; // If it just got created it can't possibly have anything inside
if(!loadSpellPropertiesFromDir(spellJSONDir)) Wizardry.logger.warn("Some spell property files did not load correctly; this will likely cause problems later!");
}
private static boolean loadConfigSpellProperties(){
Wizardry.logger.info("Loading spell properties from config folder");
File spellJSONDir = new File(Wizardry.configDirectory, "spells");
if(!spellJSONDir.exists()) return true; // If there's no global spell properties folder, do nothing
return loadSpellPropertiesFromDir(spellJSONDir);
}
// For crafting recipes, Forge does some stuff behind the scenes to load recipe JSON files from mods' namespaces. // For crafting recipes, Forge does some stuff behind the scenes to load recipe JSON files from mods' namespaces.
// This leverages the same methods. // This leverages the same methods.
private static boolean loadSpellProperties(String modID){ private static boolean loadBuiltInSpellProperties(String modID){
// Yes, I know you're not supposed to do orElse(null). But... meh. // Yes, I know you're not supposed to do orElse(null). But... meh.
ModContainer mod = Loader.instance().getModList().stream().filter(m -> m.getModId().equals(modID)).findFirst().orElse(null); ModContainer mod = Loader.instance().getModList().stream().filter(m -> m.getModId().equals(modID)).findFirst().orElse(null);
if(mod == null){ if(mod == null){
Wizardry.logger.warn("Tried to load spell properties for mod with ID '" + modID + "', but no such mod was loaded"); Wizardry.logger.warn("Tried to load built-in spell properties for mod with ID '" + modID + "', but no such mod was loaded");
return false; // Failed! return false; // Failed!
} }
@@ -279,7 +304,7 @@ public final class SpellProperties {
List<Spell> spells = Spell.getSpells(s -> s.getRegistryName().getNamespace().equals(modID)); List<Spell> spells = Spell.getSpells(s -> s.getRegistryName().getNamespace().equals(modID));
if(modID.equals(Wizardry.MODID)) spells.add(Spells.none); // In this particular case we do need the none spell if(modID.equals(Wizardry.MODID)) spells.add(Spells.none); // In this particular case we do need the none spell
Wizardry.logger.info("Loading spell properties for " + spells.size() + " spells in mod " + modID); Wizardry.logger.info("Loading built-in spell properties for " + spells.size() + " spells in mod " + modID);
// This method is used by Forge to load mod recipes and advancements, so it's a fair bet it's the right one // This method is used by Forge to load mod recipes and advancements, so it's a fair bet it's the right one
// In the absence of Javadoc, here's what the non-obvious parameters do: // In the absence of Javadoc, here's what the non-obvious parameters do:
@@ -307,12 +332,16 @@ public final class SpellProperties {
return true; return true;
} }
BufferedReader reader = null;
// We want to do this regardless of whether the JSON file got read properly, because that prints its // We want to do this regardless of whether the JSON file got read properly, because that prints its
// own separate warning // own separate warning
if(!spells.remove(spell)) Wizardry.logger.warn("What's going on?!"); if(!spells.remove(spell)) Wizardry.logger.warn("What's going on?!");
// Ignore spells overridden in the config folder
// This needs to be done AFTER the above line or it'll think there are missing spell properties files
if(spell.arePropertiesInitialised()) return true;
BufferedReader reader = null;
try{ try{
reader = Files.newBufferedReader(file); reader = Files.newBufferedReader(file);
@@ -348,6 +377,54 @@ public final class SpellProperties {
return success; return success;
} }
private static boolean loadSpellPropertiesFromDir(File dir){
boolean success = true;
for(File file : FileUtils.listFiles(dir, new String[]{"json"}, true)){
// The structure in world and config folders is subtly different in that the "spells" and mod id directories
// are in the opposite order, i.e. it's spells/modid/whatever.json instead of modid/spells/whatever.json
String relative = dir.toPath().relativize(file.toPath()).toString(); // modid\whatever.json
String nameAndModID = FilenameUtils.removeExtension(relative).replaceAll("\\\\", "/"); // modid/whatever
String modID = nameAndModID.split("/")[0]; // modid
String name = nameAndModID.substring(nameAndModID.indexOf('/') + 1); // whatever
ResourceLocation key = new ResourceLocation(modID, name);
Spell spell = Spell.registry.getValue(key);
// If no spell matches a particular file, log it and just ignore the file
if(spell == null){
Wizardry.logger.info("Spell properties file " + nameAndModID + ".json does not match any registered spells; ensure the filename is spelled correctly.");
continue;
}
BufferedReader reader = null;
try{
reader = Files.newBufferedReader(file.toPath());
JsonObject json = JsonUtils.fromJson(gson, reader, JsonObject.class);
SpellProperties properties = new SpellProperties(json, spell);
spell.setProperties(properties);
}catch(JsonParseException jsonparseexception){
Wizardry.logger.error("Parsing error loading spell property file for " + key, jsonparseexception);
success = false;
}catch(IOException ioexception){
Wizardry.logger.error("Couldn't read spell property file for " + key, ioexception);
success = false;
}finally{
IOUtils.closeQuietly(reader);
}
}
return success;
}
} }
// We probably could have used the attribute system for all of this, but I am reluctant to do so for a number of // We probably could have used the attribute system for all of this, but I am reluctant to do so for a number of
@@ -270,7 +270,7 @@ public final class WizardryUtilities {
/** Surface criterion which defines a surface as the boundary between a block that is solid on the required side and /** Surface criterion which defines a surface as the boundary between a block that is solid on the required side and
* a block that is replaceable. This means the surface can be built on. */ * a block that is replaceable. This means the surface can be built on. */
SurfaceCriteria BUILDABLE = (world, pos, side) -> world.isSideSolid(pos, side) && world.getBlockState(pos.offset(side)).getBlock().isReplaceable(world, pos); SurfaceCriteria BUILDABLE = (world, pos, side) -> world.isSideSolid(pos, side) && world.getBlockState(pos.offset(side)).getBlock().isReplaceable(world, pos.offset(side));
/** Surface criterion which defines a surface as the boundary between a block that is solid on the required side /** Surface criterion which defines a surface as the boundary between a block that is solid on the required side
* or a liquid, and an air block. Used for freezing water and placing snow. */ * or a liquid, and an air block. Used for freezing water and placing snow. */
@@ -27,7 +27,6 @@ import net.minecraft.world.gen.structure.MapGenStructureData;
import net.minecraft.world.gen.structure.StructureBoundingBox; import net.minecraft.world.gen.structure.StructureBoundingBox;
import net.minecraft.world.gen.structure.template.PlacementSettings; import net.minecraft.world.gen.structure.template.PlacementSettings;
import net.minecraft.world.gen.structure.template.Template; import net.minecraft.world.gen.structure.template.Template;
import net.minecraftforge.common.BiomeDictionary;
import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.Constants;
import net.minecraftforge.fml.common.IWorldGenerator; import net.minecraftforge.fml.common.IWorldGenerator;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@@ -120,6 +119,7 @@ public abstract class WorldGenSurfaceStructure implements IWorldGenerator {
* @param world The world in which to spawn the structure * @param world The world in which to spawn the structure
* @param chunkX The x-coordinate of the chunk being populated * @param chunkX The x-coordinate of the chunk being populated
* @param chunkZ The z-coordinate of the chunk being populated * @param chunkZ The z-coordinate of the chunk being populated
* @param structureFile The name of the structure file, used for error messages.
* @return The coordinates of the position found, or null if no suitable position was found. The returned * @return The coordinates of the position found, or null if no suitable position was found. The returned
* {@code BlockPos} is <b>always</b> the northwest corner of the structure, and the y-coordinate is that of the * {@code BlockPos} is <b>always</b> the northwest corner of the structure, and the y-coordinate is that of the
* uppermost block at those (x, z) coordinates. If the structure is being rotated this needs to be altered using * uppermost block at those (x, z) coordinates. If the structure is being rotated this needs to be altered using
@@ -128,7 +128,7 @@ public abstract class WorldGenSurfaceStructure implements IWorldGenerator {
*/ */
@Nullable @Nullable
protected BlockPos findValidPosition(Template template, PlacementSettings settings, Random random, World world, protected BlockPos findValidPosition(Template template, PlacementSettings settings, Random random, World world,
int chunkX, int chunkZ){ int chunkX, int chunkZ, String structureFile){
// Offset by (8, 8) to minimise cascading worldgen lag // Offset by (8, 8) to minimise cascading worldgen lag
// See https://www.reddit.com/r/feedthebeast/cowmments/5x0twz/investigating_extreme_worldgen_lag/?ref=share&ref_source=embed&utm_content=title&utm_medium=post_embed&utm_name=c07cbb545f74487793783012794733d8&utm_source=embedly&utm_term=5x0twz // See https://www.reddit.com/r/feedthebeast/cowmments/5x0twz/investigating_extreme_worldgen_lag/?ref=share&ref_source=embed&utm_content=title&utm_medium=post_embed&utm_name=c07cbb545f74487793783012794733d8&utm_source=embedly&utm_term=5x0twz
@@ -136,6 +136,11 @@ public abstract class WorldGenSurfaceStructure implements IWorldGenerator {
BlockPos origin = new BlockPos(8 + (chunkX << 4) + random.nextInt(16), 0, 8 + (chunkZ << 4) + random.nextInt(16)); BlockPos origin = new BlockPos(8 + (chunkX << 4) + random.nextInt(16), 0, 8 + (chunkZ << 4) + random.nextInt(16));
BlockPos size = template.transformedSize(settings.getRotation()); BlockPos size = template.transformedSize(settings.getRotation());
if(size.getX() == 0 || size.getY() == 0 || size.getZ() == 0){
Wizardry.logger.warn("Structure template file {} is missing or empty! If you're trying to disable structure spawning, pointing to a non-existent location is NOT the correct way to do so; use the structure dimension lists instead.", structureFile);
}
// Estimate a starting height for searching for the floor // Estimate a starting height for searching for the floor
BlockPos centre = world.getTopSolidOrLiquidBlock(new BlockPos(origin.add(size.getX()/2, 0, size.getZ()/2))); BlockPos centre = world.getTopSolidOrLiquidBlock(new BlockPos(origin.add(size.getX()/2, 0, size.getZ()/2)));
Integer startingHeight = WizardryUtilities.getNearestSurface(world, centre, EnumFacing.UP, 32, true, Integer startingHeight = WizardryUtilities.getNearestSurface(world, centre, EnumFacing.UP, 32, true,
@@ -145,8 +150,8 @@ public abstract class WorldGenSurfaceStructure implements IWorldGenerator {
if(Wizardry.settings.fastWorldgen){ if(Wizardry.settings.fastWorldgen){
BlockPos result = origin.up(startingHeight); BlockPos result = origin.up(startingHeight);
// Fast worldgen doesn't check for water, instead it checks the biome like vanilla, which is crude but fast // Fast worldgen only checks the central position for water, which isn't perfect but it is faster
return BiomeDictionary.hasType(world.getBiome(result), BiomeDictionary.Type.WATER) ? null : result; return world.getBlockState(new BlockPos(centre.getX(), startingHeight + 1, centre.getZ())).getMaterial().isLiquid() ? null : result;
} }
int[] floorHeights = new int[size.getX() * size.getZ()]; int[] floorHeights = new int[size.getX() * size.getZ()];
@@ -212,7 +217,7 @@ public abstract class WorldGenSurfaceStructure implements IWorldGenerator {
BlockPos origin; BlockPos origin;
do { do {
origin = findValidPosition(template, settings, random, world, chunkX, chunkZ); origin = findValidPosition(template, settings, random, world, chunkX, chunkZ, structureFile.toString());
triesLeft--; triesLeft--;
}while(triesLeft > 0 && origin != null); }while(triesLeft > 0 && origin != null);
@@ -244,7 +249,7 @@ public abstract class WorldGenSurfaceStructure implements IWorldGenerator {
spawnStructure(random, world, origin, template, settings, structureFile); spawnStructure(random, world, origin, template, settings, structureFile);
if(!Wizardry.settings.fastWorldgen) removeFloatingTrees(world, box); if(!Wizardry.settings.fastWorldgen) removeFloatingTrees(world, box, random);
structureMap.put(ChunkPos.asLong(origin.getX() >> 4, origin.getZ() >> 4), settings.getBoundingBox()); structureMap.put(ChunkPos.asLong(origin.getX() >> 4, origin.getZ() >> 4), settings.getBoundingBox());
@@ -306,7 +311,7 @@ public abstract class WorldGenSurfaceStructure implements IWorldGenerator {
} }
/** Finds and removes any floating bits of tree in and above the given structure bounding box. */ /** Finds and removes any floating bits of tree in and above the given structure bounding box. */
protected static void removeFloatingTrees(World world, StructureBoundingBox boundingBox){ protected static void removeFloatingTrees(World world, StructureBoundingBox boundingBox, Random random){
boolean changed = true; boolean changed = true;
int y = boundingBox.minY; int y = boundingBox.minY;
@@ -354,7 +359,7 @@ public abstract class WorldGenSurfaceStructure implements IWorldGenerator {
} }
for(int i=0; i<16; i++){ for(int i=0; i<16; i++){
leaves.forEach(p -> world.getBlockState(p).getBlock().updateTick(world, p, world.getBlockState(p), null)); leaves.forEach(p -> world.getBlockState(p).getBlock().updateTick(world, p, world.getBlockState(p), random));
} }
// Finally, remove all the items that were dropped as a result of leaf decay // Finally, remove all the items that were dropped as a result of leaf decay
@@ -1117,6 +1117,8 @@ config.ebwizardry.creative_bypasses_arcane_lock.true=Anyone in creative mode
config.ebwizardry.creative_bypasses_arcane_lock.false=Only ops in creative mode config.ebwizardry.creative_bypasses_arcane_lock.false=Only ops in creative mode
config.ebwizardry.slow_time_affects_players=Slow Time Affects Players config.ebwizardry.slow_time_affects_players=Slow Time Affects Players
config.ebwizardry.slow_time_affects_players.tooltip=Whether players are slowed when another nearby player uses the slow time spell. If this is disabled, mobs and projectiles will still be affected but players will move at normal speed. config.ebwizardry.slow_time_affects_players.tooltip=Whether players are slowed when another nearby player uses the slow time spell. If this is disabled, mobs and projectiles will still be affected but players will move at normal speed.
config.ebwizardry.bonemeal_grows_crystal_flowers=Bonemeal Grows Crystal Flowers
config.ebwizardry.bonemeal_grows_crystal_flowers.tooltip=Whether using bonemeal on grass blocks has a chance to grow crystal flowers.
config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist
config.ebwizardry.mob_loot_table_whitelist.tooltip=Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them. config.ebwizardry.mob_loot_table_whitelist.tooltip=Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them.
config.ebwizardry.mob_loot_table_blacklist=Mob Loot Table Blacklist config.ebwizardry.mob_loot_table_blacklist=Mob Loot Table Blacklist
@@ -1164,27 +1166,27 @@ config.ebwizardry.fast_worldgen.tooltip=Controls which algorithm wizardry uses f
config.ebwizardry.fast_worldgen.true=Fast config.ebwizardry.fast_worldgen.true=Fast
config.ebwizardry.fast_worldgen.false=Fancy config.ebwizardry.fast_worldgen.false=Fancy
config.ebwizardry.tower_dimensions=Tower Dimensions config.ebwizardry.tower_dimensions=Tower Dimensions
config.ebwizardry.tower_dimensions.tooltip=List of ids of dimensions in which wizard towers will generate. config.ebwizardry.tower_dimensions.tooltip=List of ids of dimensions in which wizard towers will generate. Remove all dimensions to disable wizard towers completely.
config.ebwizardry.tower_rarity=Tower Rarity config.ebwizardry.tower_rarity=Tower Rarity
config.ebwizardry.tower_rarity.tooltip=Rarity of wizard towers. 1 in this many chunks will contain a wizard tower, meaning higher numbers are rarer. config.ebwizardry.tower_rarity.tooltip=Rarity of wizard towers. 1 in this many chunks will contain a wizard tower, meaning higher numbers are rarer.
config.ebwizardry.evil_wizard_chance=Evil Wizard Chance config.ebwizardry.evil_wizard_chance=Evil Wizard Chance
config.ebwizardry.evil_wizard_chance.tooltip=The chance for wizard towers to generate with an evil wizard and chest inside, instead of a friendly wizard. config.ebwizardry.evil_wizard_chance.tooltip=The chance for wizard towers to generate with an evil wizard and chest inside, instead of a friendly wizard.
config.ebwizardry.tower_files=Tower Structure Files config.ebwizardry.tower_files=Tower Structure Files
config.ebwizardry.tower_files.tooltip=List of structure file locations for wizard towers without loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. config.ebwizardry.tower_files.tooltip=List of structure file locations for wizard towers without loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable wizard towers, use the tower dimensions setting.
config.ebwizardry.tower_with_chest_files=Tower With Chest Structure Files config.ebwizardry.tower_with_chest_files=Tower With Chest Structure Files
config.ebwizardry.tower_with_chest_files.tooltip=List of structure file locations for wizard towers with loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. config.ebwizardry.tower_with_chest_files.tooltip=List of structure file locations for wizard towers with loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable wizard towers, use the tower dimensions setting.
config.ebwizardry.obelisk_dimensions=Obelisk Dimensions config.ebwizardry.obelisk_dimensions=Obelisk Dimensions
config.ebwizardry.obelisk_dimensions.tooltip=List of ids of dimensions in which obelisks will generate. config.ebwizardry.obelisk_dimensions.tooltip=List of ids of dimensions in which obelisks will generate. Remove all dimensions to disable obelisks completely.
config.ebwizardry.obelisk_rarity=Obelisk Rarity config.ebwizardry.obelisk_rarity=Obelisk Rarity
config.ebwizardry.obelisk_rarity.tooltip=Rarity of obelisks. 1 in this many chunks will contain an obelisk, meaning higher numbers are rarer. config.ebwizardry.obelisk_rarity.tooltip=Rarity of obelisks. 1 in this many chunks will contain an obelisk, meaning higher numbers are rarer.
config.ebwizardry.obelisk_files=Obelisk Structure Files config.ebwizardry.obelisk_files=Obelisk Structure Files
config.ebwizardry.obelisk_files.tooltip=List of structure file locations for obelisks. One of these files will be randomly selected each time an obelisk is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. config.ebwizardry.obelisk_files.tooltip=List of structure file locations for obelisks. One of these files will be randomly selected each time an obelisk is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable obelisks, use the obelisk dimensions setting.
config.ebwizardry.shrine_dimensions=Shrine Dimensions config.ebwizardry.shrine_dimensions=Shrine Dimensions
config.ebwizardry.shrine_dimensions.tooltip=List of ids of dimensions in which shrines will generate. config.ebwizardry.shrine_dimensions.tooltip=List of ids of dimensions in which shrines will generate. Remove all dimensions to disable shrines completely.
config.ebwizardry.shrine_rarity=Shrine Rarity config.ebwizardry.shrine_rarity=Shrine Rarity
config.ebwizardry.shrine_rarity.tooltip=Rarity of shrines. 1 in this many chunks will contain a shrine, meaning higher numbers are rarer. config.ebwizardry.shrine_rarity.tooltip=Rarity of shrines. 1 in this many chunks will contain a shrine, meaning higher numbers are rarer.
config.ebwizardry.shrine_files=Shrine Structure Files config.ebwizardry.shrine_files=Shrine Structure Files
config.ebwizardry.shrine_files.tooltip=List of structure file locations for shrines. One of these files will be randomly selected each time a shrine is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. config.ebwizardry.shrine_files.tooltip=List of structure file locations for shrines. One of these files will be randomly selected each time a shrine is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable shrines, use the shrine dimensions setting.
config.ebwizardry.tree_blocks=Tree Blocks config.ebwizardry.tree_blocks=Tree Blocks
config.ebwizardry.tree_blocks.tooltip=List of registry names of blocks which can be overwritten by wizardry's structure generators, affecting both fast and fancy structure generation. Most tree blocks and other foliage should work automatically, but those that don't can be added manually here. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. dynamictrees:oakbranch). config.ebwizardry.tree_blocks.tooltip=List of registry names of blocks which can be overwritten by wizardry's structure generators, affecting both fast and fancy structure generation. Most tree blocks and other foliage should work automatically, but those that don't can be added manually here. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. dynamictrees:oakbranch).
config.ebwizardry.ore_dimensions=Ore Dimensions config.ebwizardry.ore_dimensions=Ore Dimensions
@@ -1287,4 +1289,9 @@ integration.antiqueatlas.marker.ebwizardry\:wizard_tower=Wizard Tower
integration.antiqueatlas.marker.ebwizardry\:obelisk=Obelisk integration.antiqueatlas.marker.ebwizardry\:obelisk=Obelisk
integration.antiqueatlas.marker.ebwizardry\:shrine=Shrine integration.antiqueatlas.marker.ebwizardry\:shrine=Shrine
spell.ebwizardry\:magic_missile_festive=Magic Mistletoe
spell.ebwizardry\:summon_snow_golem_festive=Summon Frosty The Snowman
spell.ebwizardry\:invigorating_presence_festive=Invigorating Presents
spell.ebwizardry\:empowering_presence_festive=Empowering Presents
wizard.debug=%1$s, %2$s, %3$s wizard.debug=%1$s, %2$s, %3$s
@@ -1117,6 +1117,8 @@ config.ebwizardry.creative_bypasses_arcane_lock.true=Anyone in creative mode
config.ebwizardry.creative_bypasses_arcane_lock.false=Only ops in creative mode config.ebwizardry.creative_bypasses_arcane_lock.false=Only ops in creative mode
config.ebwizardry.slow_time_affects_players=Slow Time Affects Players config.ebwizardry.slow_time_affects_players=Slow Time Affects Players
config.ebwizardry.slow_time_affects_players.tooltip=Whether players are slowed when another nearby player uses the slow time spell. If this is disabled, mobs and projectiles will still be affected but players will move at normal speed. config.ebwizardry.slow_time_affects_players.tooltip=Whether players are slowed when another nearby player uses the slow time spell. If this is disabled, mobs and projectiles will still be affected but players will move at normal speed.
config.ebwizardry.bonemeal_grows_crystal_flowers=Bonemeal Grows Crystal Flowers
config.ebwizardry.bonemeal_grows_crystal_flowers.tooltip=Whether using bonemeal on grass blocks has a chance to grow crystal flowers.
config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist
config.ebwizardry.mob_loot_table_whitelist.tooltip=Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them. config.ebwizardry.mob_loot_table_whitelist.tooltip=Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them.
config.ebwizardry.mob_loot_table_blacklist=Mob Loot Table Blacklist config.ebwizardry.mob_loot_table_blacklist=Mob Loot Table Blacklist
@@ -1164,27 +1166,27 @@ config.ebwizardry.fast_worldgen.tooltip=Controls which algorithm wizardry uses f
config.ebwizardry.fast_worldgen.true=Fast config.ebwizardry.fast_worldgen.true=Fast
config.ebwizardry.fast_worldgen.false=Fancy config.ebwizardry.fast_worldgen.false=Fancy
config.ebwizardry.tower_dimensions=Tower Dimensions config.ebwizardry.tower_dimensions=Tower Dimensions
config.ebwizardry.tower_dimensions.tooltip=List of ids of dimensions in which wizard towers will generate. config.ebwizardry.tower_dimensions.tooltip=List of ids of dimensions in which wizard towers will generate. Remove all dimensions to disable wizard towers completely.
config.ebwizardry.tower_rarity=Tower Rarity config.ebwizardry.tower_rarity=Tower Rarity
config.ebwizardry.tower_rarity.tooltip=Rarity of wizard towers. 1 in this many chunks will contain a wizard tower, meaning higher numbers are rarer. config.ebwizardry.tower_rarity.tooltip=Rarity of wizard towers. 1 in this many chunks will contain a wizard tower, meaning higher numbers are rarer.
config.ebwizardry.evil_wizard_chance=Evil Wizard Chance config.ebwizardry.evil_wizard_chance=Evil Wizard Chance
config.ebwizardry.evil_wizard_chance.tooltip=The chance for wizard towers to generate with an evil wizard and chest inside, instead of a friendly wizard. config.ebwizardry.evil_wizard_chance.tooltip=The chance for wizard towers to generate with an evil wizard and chest inside, instead of a friendly wizard.
config.ebwizardry.tower_files=Tower Structure Files config.ebwizardry.tower_files=Tower Structure Files
config.ebwizardry.tower_files.tooltip=List of structure file locations for wizard towers without loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. config.ebwizardry.tower_files.tooltip=List of structure file locations for wizard towers without loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable wizard towers, use the tower dimensions setting.
config.ebwizardry.tower_with_chest_files=Tower With Chest Structure Files config.ebwizardry.tower_with_chest_files=Tower With Chest Structure Files
config.ebwizardry.tower_with_chest_files.tooltip=List of structure file locations for wizard towers with loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. config.ebwizardry.tower_with_chest_files.tooltip=List of structure file locations for wizard towers with loot chests. One of these files will be randomly selected each time a wizard tower is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable wizard towers, use the tower dimensions setting.
config.ebwizardry.obelisk_dimensions=Obelisk Dimensions config.ebwizardry.obelisk_dimensions=Obelisk Dimensions
config.ebwizardry.obelisk_dimensions.tooltip=List of ids of dimensions in which obelisks will generate. config.ebwizardry.obelisk_dimensions.tooltip=List of ids of dimensions in which obelisks will generate. Remove all dimensions to disable obelisks completely.
config.ebwizardry.obelisk_rarity=Obelisk Rarity config.ebwizardry.obelisk_rarity=Obelisk Rarity
config.ebwizardry.obelisk_rarity.tooltip=Rarity of obelisks. 1 in this many chunks will contain an obelisk, meaning higher numbers are rarer. config.ebwizardry.obelisk_rarity.tooltip=Rarity of obelisks. 1 in this many chunks will contain an obelisk, meaning higher numbers are rarer.
config.ebwizardry.obelisk_files=Obelisk Structure Files config.ebwizardry.obelisk_files=Obelisk Structure Files
config.ebwizardry.obelisk_files.tooltip=List of structure file locations for obelisks. One of these files will be randomly selected each time an obelisk is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. config.ebwizardry.obelisk_files.tooltip=List of structure file locations for obelisks. One of these files will be randomly selected each time an obelisk is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable obelisks, use the obelisk dimensions setting.
config.ebwizardry.shrine_dimensions=Shrine Dimensions config.ebwizardry.shrine_dimensions=Shrine Dimensions
config.ebwizardry.shrine_dimensions.tooltip=List of ids of dimensions in which shrines will generate. config.ebwizardry.shrine_dimensions.tooltip=List of ids of dimensions in which shrines will generate. Remove all dimensions to disable shrines completely.
config.ebwizardry.shrine_rarity=Shrine Rarity config.ebwizardry.shrine_rarity=Shrine Rarity
config.ebwizardry.shrine_rarity.tooltip=Rarity of shrines. 1 in this many chunks will contain a shrine, meaning higher numbers are rarer. config.ebwizardry.shrine_rarity.tooltip=Rarity of shrines. 1 in this many chunks will contain a shrine, meaning higher numbers are rarer.
config.ebwizardry.shrine_files=Shrine Structure Files config.ebwizardry.shrine_files=Shrine Structure Files
config.ebwizardry.shrine_files.tooltip=List of structure file locations for shrines. One of these files will be randomly selected each time a shrine is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. config.ebwizardry.shrine_files.tooltip=List of structure file locations for shrines. One of these files will be randomly selected each time a shrine is generated. File locations are of the format [mod id]\:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable shrines, use the shrine dimensions setting.
config.ebwizardry.tree_blocks=Tree Blocks config.ebwizardry.tree_blocks=Tree Blocks
config.ebwizardry.tree_blocks.tooltip=List of registry names of blocks which can be overwritten by wizardry's structure generators, affecting both fast and fancy structure generation. Most tree blocks and other foliage should work automatically, but those that don't can be added manually here. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. dynamictrees:oakbranch). config.ebwizardry.tree_blocks.tooltip=List of registry names of blocks which can be overwritten by wizardry's structure generators, affecting both fast and fancy structure generation. Most tree blocks and other foliage should work automatically, but those that don't can be added manually here. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. dynamictrees:oakbranch).
config.ebwizardry.ore_dimensions=Ore Dimensions config.ebwizardry.ore_dimensions=Ore Dimensions
@@ -1287,4 +1289,9 @@ integration.antiqueatlas.marker.ebwizardry\:wizard_tower=Wizard Tower
integration.antiqueatlas.marker.ebwizardry\:obelisk=Obelisk integration.antiqueatlas.marker.ebwizardry\:obelisk=Obelisk
integration.antiqueatlas.marker.ebwizardry\:shrine=Shrine integration.antiqueatlas.marker.ebwizardry\:shrine=Shrine
spell.ebwizardry\:magic_missile_festive=Magic Mistletoe
spell.ebwizardry\:summon_snow_golem_festive=Summon Frosty The Snowman
spell.ebwizardry\:invigorating_presence_festive=Invigorating Presents
spell.ebwizardry\:empowering_presence_festive=Empowering Presents
wizard.debug=%1$s, %2$s, %3$s wizard.debug=%1$s, %2$s, %3$s
@@ -103,7 +103,7 @@
{ {
"type": "item", "type": "item",
"name": "ebwizardry:astral_diamond", "name": "ebwizardry:astral_diamond",
"weight": 1 "weight": 2
}, },
{ {
"type": "item", "type": "item",
@@ -113,7 +113,7 @@
{ {
"type": "item", "type": "item",
"name": "ebwizardry:grand_crystal", "name": "ebwizardry:grand_crystal",
"weight": 1 "weight": 2
} }
] ]
} }
@@ -108,7 +108,7 @@
{ {
"type": "item", "type": "item",
"name": "ebwizardry:astral_diamond", "name": "ebwizardry:astral_diamond",
"weight": 1 "weight": 2
}, },
{ {
"type": "item", "type": "item",
@@ -118,7 +118,7 @@
{ {
"type": "item", "type": "item",
"name": "ebwizardry:grand_crystal", "name": "ebwizardry:grand_crystal",
"weight": 1 "weight": 2
} }
] ]
}, },
@@ -2,5 +2,13 @@
"parent": "item/generated", "parent": "item/generated",
"textures": { "textures": {
"layer0": "ebwizardry:items/scroll" "layer0": "ebwizardry:items/scroll"
} },
"overrides": [
{
"predicate": {
"festive": 1
},
"model": "ebwizardry:item/festive_scroll"
}
]
} }
@@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/scroll_festive"
}
}
@@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "ebwizardry:items/spell_book_festive"
}
}
@@ -0,0 +1,6 @@
{
"parent": "item/handheld",
"textures": {
"layer0": "ebwizardry:items/wand_festive"
}
}
@@ -2,5 +2,13 @@
"parent": "item/generated", "parent": "item/generated",
"textures": { "textures": {
"layer0": "ebwizardry:items/scroll" "layer0": "ebwizardry:items/scroll"
} },
"overrides": [
{
"predicate": {
"festive": 1
},
"model": "ebwizardry:item/festive_scroll"
}
]
} }
@@ -2,5 +2,13 @@
"parent": "item/generated", "parent": "item/generated",
"textures": { "textures": {
"layer0": "ebwizardry:items/spell_book" "layer0": "ebwizardry:items/spell_book"
} },
"overrides": [
{
"predicate": {
"festive": 1
},
"model": "ebwizardry:item/festive_spell_book"
}
]
} }
@@ -7,7 +7,7 @@
"item.wand.levelup": {"category": "player", "sounds": ["random/levelup"]}, "item.wand.levelup": {"category": "player", "sounds": ["random/levelup"]},
"item.wand.melee": {"category": "player", "sounds": ["ebwizardry:effect1", "ebwizardry:effect2"]}, "item.wand.melee": {"category": "player", "sounds": ["ebwizardry:effect1", "ebwizardry:effect2"]},
"item.armour.equip_silk": {"category": "player", "sounds": ["item/armor/equip_leather1", "item/armor/equip_leather2", "item/armor/equip_leather3", "item/armor/equip_leather4", "item/armor/equip_leather5", "item/armor/equip_leather6"]}, "item.armour.equip_silk": {"category": "player", "sounds": ["item/armor/equip_leather1", "item/armor/equip_leather2", "item/armor/equip_leather3", "item/armor/equip_leather4", "item/armor/equip_leather5", "item/armor/equip_leather6"]},
"item.purifying_elixir.drink": {"category": "player", "sounds": ["ebwizardry:spellbind"]}, "item.purifying_elixir.drink": {"category": "player", "sounds": ["ebwizardry:buff_large"]},
"entity.black_hole.ambient": {"category": "spells", "sounds": ["portal/portal"]}, "entity.black_hole.ambient": {"category": "spells", "sounds": ["portal/portal"]},
"entity.black_hole.vanish": {"category": "spells", "sounds": ["portal/trigger"]}, "entity.black_hole.vanish": {"category": "spells", "sounds": ["portal/trigger"]},
@@ -62,7 +62,8 @@
"entity.wizard.no": {"category": "neutral", "sounds": ["mob/villager/no1", "mob/villager/no2", "mob/villager/no3"]}, "entity.wizard.no": {"category": "neutral", "sounds": ["mob/villager/no1", "mob/villager/no2", "mob/villager/no3"]},
"entity.wizard.hurt": {"category": "neutral", "sounds": ["mob/villager/hit1", "mob/villager/hit2", "mob/villager/hit3", "mob/villager/hit4"]}, "entity.wizard.hurt": {"category": "neutral", "sounds": ["mob/villager/hit1", "mob/villager/hit2", "mob/villager/hit3", "mob/villager/hit4"]},
"entity.wizard.death": {"category": "neutral", "sounds": ["mob/villager/death"]}, "entity.wizard.death": {"category": "neutral", "sounds": ["mob/villager/death"]},
"entity.wizard.hohoho": {"category": "neutral", "sounds": ["ebwizardry:hohoho"]},
"entity.darkness_orb.hit": {"category": "spells", "sounds": ["mob/wither/hurt1", "mob/wither/hurt2", "mob/wither/hurt3", "mob/wither/hurt4"]}, "entity.darkness_orb.hit": {"category": "spells", "sounds": ["mob/wither/hurt1", "mob/wither/hurt2", "mob/wither/hurt3", "mob/wither/hurt4"]},
"entity.dart.hit": {"category": "spells", "sounds": ["damage/hit1", "damage/hit2", "damage/hit3"]}, "entity.dart.hit": {"category": "spells", "sounds": ["damage/hit1", "damage/hit2", "damage/hit3"]},
"entity.dart.hit_block": {"category": "spells", "sounds": ["random/bowhit1", "random/bowhit2", "random/bowhit3"]}, "entity.dart.hit_block": {"category": "spells", "sounds": ["random/bowhit1", "random/bowhit2", "random/bowhit3"]},
@@ -158,8 +159,8 @@
"spell.flaming_axe": {"category": "spells", "sounds": ["ebwizardry:buff"]}, "spell.flaming_axe": {"category": "spells", "sounds": ["ebwizardry:buff"]},
"spell.flaming_weapon": {"category": "spells", "sounds": ["ebwizardry:buff"]}, "spell.flaming_weapon": {"category": "spells", "sounds": ["ebwizardry:buff"]},
"spell.flight": {"category": "spells", "sounds": ["mob/enderdragon/wings1", "mob/enderdragon/wings2", "mob/enderdragon/wings3", "mob/enderdragon/wings4", "mob/enderdragon/wings5", "mob/enderdragon/wings6"]}, "spell.flight": {"category": "spells", "sounds": ["mob/enderdragon/wings1", "mob/enderdragon/wings2", "mob/enderdragon/wings3", "mob/enderdragon/wings4", "mob/enderdragon/wings5", "mob/enderdragon/wings6"]},
"spell.font_of_mana": {"category": "spells", "sounds": ["ebwizardry:spellbind"]}, "spell.font_of_mana": {"category": "spells", "sounds": ["ebwizardry:buff_large"]},
"spell.font_of_vitality": {"category": "spells", "sounds": ["ebwizardry:spellbind"]}, "spell.font_of_vitality": {"category": "spells", "sounds": ["ebwizardry:buff_large"]},
"spell.force_arrow": {"category": "spells", "sounds": ["ebwizardry:force"]}, "spell.force_arrow": {"category": "spells", "sounds": ["ebwizardry:force"]},
"spell.forcefield": {"category": "spells", "sounds": ["ebwizardry:conjure_large"]}, "spell.forcefield": {"category": "spells", "sounds": ["ebwizardry:conjure_large"]},
"spell.force_orb": {"category": "spells", "sounds": ["random/bow"]}, "spell.force_orb": {"category": "spells", "sounds": ["random/bow"]},
@@ -179,11 +180,11 @@
"spell.grapple.pull": {"category": "spells", "sounds": ["ebwizardry:pull1", "ebwizardry:pull2"]}, "spell.grapple.pull": {"category": "spells", "sounds": ["ebwizardry:pull1", "ebwizardry:pull2"]},
"spell.grapple.release": {"category": "spells", "sounds": ["random/bowhit1", "random/bowhit2", "random/bowhit3"]}, "spell.grapple.release": {"category": "spells", "sounds": ["random/bowhit1", "random/bowhit2", "random/bowhit3"]},
"spell.greater_fireball": {"category": "spells", "sounds": ["mob/ghast/fireball4"]}, "spell.greater_fireball": {"category": "spells", "sounds": ["mob/ghast/fireball4"]},
"spell.greater_heal": {"category": "spells", "sounds": ["ebwizardry:spellbind"]}, "spell.greater_heal": {"category": "spells", "sounds": ["ebwizardry:buff_large"]},
"spell.greater_telekinesis.start": {"category": "spells", "sounds": ["ebwizardry:aura_start"]}, "spell.greater_telekinesis.start": {"category": "spells", "sounds": ["ebwizardry:aura_start"]},
"spell.greater_telekinesis.loop": {"category": "spells", "sounds": ["ebwizardry:aura_loop"]}, "spell.greater_telekinesis.loop": {"category": "spells", "sounds": ["ebwizardry:aura_loop"]},
"spell.greater_telekinesis.end": {"category": "spells", "sounds": ["ebwizardry:aura_end"]}, "spell.greater_telekinesis.end": {"category": "spells", "sounds": ["ebwizardry:aura_end"]},
"spell.greater_ward": {"category": "spells", "sounds": ["ebwizardry:spellbind"]}, "spell.greater_ward": {"category": "spells", "sounds": ["ebwizardry:buff_large"]},
"spell.group_heal": {"category": "spells", "sounds": ["ebwizardry:heal"]}, "spell.group_heal": {"category": "spells", "sounds": ["ebwizardry:heal"]},
"spell.growth_aura": {"category": "spells", "sounds": ["ebwizardry:conjure_large"]}, "spell.growth_aura": {"category": "spells", "sounds": ["ebwizardry:conjure_large"]},
"spell.hailstorm": {"category": "spells", "sounds": ["mob/illusion_illager/prepare_blind"]}, "spell.hailstorm": {"category": "spells", "sounds": ["mob/illusion_illager/prepare_blind"]},
@@ -250,12 +251,12 @@
"spell.ray_of_purification.start": {"category": "spells", "sounds": ["ebwizardry:aura_start"]}, "spell.ray_of_purification.start": {"category": "spells", "sounds": ["ebwizardry:aura_start"]},
"spell.ray_of_purification.loop": {"category": "spells", "sounds": ["ebwizardry:aura_loop"]}, "spell.ray_of_purification.loop": {"category": "spells", "sounds": ["ebwizardry:aura_loop"]},
"spell.ray_of_purification.end": {"category": "spells", "sounds": ["ebwizardry:aura_end"]}, "spell.ray_of_purification.end": {"category": "spells", "sounds": ["ebwizardry:aura_end"]},
"spell.remove_curse": {"category": "spells", "sounds": ["ebwizardry:spellbind"]}, "spell.remove_curse": {"category": "spells", "sounds": ["ebwizardry:buff_large"]},
"spell.replenish_hunger": {"category": "spells", "sounds": ["ebwizardry:heal"]}, "spell.replenish_hunger": {"category": "spells", "sounds": ["ebwizardry:heal"]},
"spell.resurrection": {"category": "spells", "sounds": ["ebwizardry:spellbind"]}, "spell.resurrection": {"category": "spells", "sounds": ["ebwizardry:buff_large"]},
"spell.reversal": {"category": "spells", "sounds": ["mob/wither/hurt1", "mob/wither/hurt2", "mob/wither/hurt3", "mob/wither/hurt4"]}, "spell.reversal": {"category": "spells", "sounds": ["mob/wither/hurt1", "mob/wither/hurt2", "mob/wither/hurt3", "mob/wither/hurt4"]},
"spell.ring_of_fire": {"category": "spells", "sounds": ["mob/ghast/fireball4"]}, "spell.ring_of_fire": {"category": "spells", "sounds": ["mob/ghast/fireball4"]},
"spell.satiety": {"category": "spells", "sounds": ["ebwizardry:spellbind"]}, "spell.satiety": {"category": "spells", "sounds": ["ebwizardry:buff_large"]},
"spell.shadow_ward.start": {"category": "spells", "sounds": ["ebwizardry:dark_aura_start"]}, "spell.shadow_ward.start": {"category": "spells", "sounds": ["ebwizardry:dark_aura_start"]},
"spell.shadow_ward.loop": {"category": "spells", "sounds": ["ebwizardry:dark_aura_loop"]}, "spell.shadow_ward.loop": {"category": "spells", "sounds": ["ebwizardry:dark_aura_loop"]},
"spell.shadow_ward.end": {"category": "spells", "sounds": ["ebwizardry:dark_aura_end"]}, "spell.shadow_ward.end": {"category": "spells", "sounds": ["ebwizardry:dark_aura_end"]},
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More