Compare commits

...

75 Commits

Author SHA1 Message Date
Electroblob77 a490168fa2 Derp 2020-03-19 15:46:42 +00:00
Electroblob77 306137a026 Update version number 2020-03-19 15:38:19 +00:00
Electroblob77 0a5527f3d4 Add backwards compatibility to convert old-format atlas markers to new ones 2020-03-19 15:35:09 +00:00
Electroblob77 4dbe24c8ea This is the problem with everything being a string... 2020-03-19 15:13:25 +00:00
Electroblob77 81bba5e7e0 Add null check to ice spikes, fixes #371 2020-03-19 13:35:36 +00:00
Electroblob77 9c29142e6a Maven-ify all the things and update Antique Atlas 2020-03-19 13:33:34 +00:00
Electroblob77 3151d7d626 Require Antique Atlas 4.6 or newer 2020-03-18 17:18:41 +00:00
Electroblob77 d68f968632 Make sure fall damage stuff is processed on the server only, and reactivate the config option by default 2020-03-18 16:00:23 +00:00
Electroblob77 74d9198522 Convert replaceVanillaFireballs to a synchronised option, fixes #351 2020-03-18 15:50:01 +00:00
Electroblob77 0b56d47022 Replace wildcard with metadata 0 for display purposes, fixes #370
N.B. The output is still not visible for non-zero inputs when using wildcard trades, but for now this is good enough
2020-03-18 15:32:59 +00:00
Electroblob77 587ab1338e Make Magic Protection protect against vanilla magic damage, closes #337 2020-03-18 14:57:04 +00:00
Electroblob77 4816c48aa6 Disable replaceVanillaFallDamage by default 2020-03-18 14:49:12 +00:00
Electroblob77 79b745e005 Randomise y coordinate to search from when spawning structures in 'cavern' dimensions, fixes #372 2020-03-17 23:54:52 +00:00
Electroblob77 6fff0e31d1 *Facepalm* 2020-03-17 23:45:40 +00:00
Electroblob77 11efe56437 Replace old hardcoded spell book text with translation keys, fixes #368 2020-03-17 23:38:25 +00:00
Electroblob77 7f5786c28c Override override override! 2020-03-17 23:22:54 +00:00
Electroblob77 85b49b330a Make tomes of arcana more common in obelisks, shrines and wizard towers, and make tomes generated as loot more likely to be apprentice tier 2020-03-17 23:16:04 +00:00
Electroblob77 d797e72445 Reduce rarity of all structures a bit 2020-03-17 23:13:17 +00:00
Electroblob77 5f21b565cd Make fireballs, large fireballs and iceballs respect the playerBlockDamage config option 2020-03-17 22:44:30 +00:00
Electroblob77 9ed06c9a34 Stop wizards casting heal ally 2020-03-17 22:34:34 +00:00
Electroblob77 50f315666f Fix spanish handbook translations, fixes #365 2020-03-15 19:23:50 +00:00
Electroblob77 62bdbc81e4 Prevent players from picking up items when possessing and prevent players from possessing whilst sneaking, fixes #376 2020-03-15 19:15:28 +00:00
Electroblob77 3a571fd239 Update credits 2020-02-29 11:25:35 +00:00
Electroblob 4fb5452dc4 Merge pull request #363 from chesterccj305/patch-1
Create handbook_zh_tw.json
2020-02-24 16:51:28 +00:00
Electroblob 652f1d2620 Merge pull request #364 from chesterccj305/patch-2
Create zh_tw.lang
2020-02-24 16:51:15 +00:00
Electroblob77 275f514810 Substitute generic damage when caster is null, fixes #362 2020-02-22 11:12:14 +00:00
Electroblob77 7cf09e76f8 Update version number 2020-02-22 00:49:18 +00:00
Electroblob77 61080134fd Add wildcard support for item/block configs 2020-02-22 00:46:37 +00:00
Electroblob77 563bed3b25 Update credits 2020-02-22 00:46:16 +00:00
Electroblob77 75740c388f Add German and Spanish translations 2020-02-22 00:17:52 +00:00
Electroblob77 5d96f4d700 I can't string, and unboxing is weird 2020-02-21 17:55:45 +00:00
Electroblob77 df8fd21c0b Add metadata support to block/item list config options, fixes #353 2020-02-21 17:40:08 +00:00
chesterccj305 046ecb1e41 Create zh_tw.lang 2020-02-21 22:56:45 +08:00
chesterccj305 f3dd7bccd8 Create handbook_zh_tw.json 2020-02-21 22:55:22 +08:00
Electroblob77 d6b64b3576 Move !world.isRemote check to just the block breaking, potentially fixes #359 2020-02-19 21:32:54 +00:00
Electroblob77 22c00c47ff Antique Atlas doesn't like my colons :(
Fixes #323
2020-02-19 21:17:52 +00:00
Electroblob77 6d0e65c2b0 Hmmm so we were ticking this twice all along... 2020-02-19 01:09:12 +00:00
Electroblob77 0a6fd452c1 This is why we test things! 2020-02-19 01:00:32 +00:00
Electroblob77 28a8a4ff2a Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2020-02-19 00:22:24 +00:00
Electroblob77 71c09cf16d Bye bye empty slot books, it was good knowing ya 2020-02-19 00:12:20 +00:00
Electroblob77 794085660e Move screen shaking effect into a proper method in the client proxy, fix earthquake's version not working and add screen shake to meteor, lightning hammer and shockwave 2020-02-19 00:11:33 +00:00
Electroblob 3d89186308 Merge pull request #352 from Hokorizero/1.12.2
Update Chinese item translation and handbook translation
2020-02-09 13:37:10 +00:00
Hokorizero a0bd7b64d7 Enchantment Descriptions translation done 2020-02-07 18:23:40 +08:00
Hokorizero 4c633c98ff Add files via upload 2020-02-05 19:06:34 +08:00
Hokorizero 45ff70c207 Add files via upload
Thanks by @TUsama,we finally finished the Chinese handbook translation.
2020-02-05 19:05:39 +08:00
Electroblob77 0328f41564 Prevent potion core applying its health fix (whatever that is) when resurrecting, fixes #178 2020-01-28 21:19:37 +00:00
Electroblob77 9ab4684aef Update version number 2020-01-28 19:44:27 +00:00
Electroblob77 e64cf9a6af Update credits 2020-01-28 18:44:37 +00:00
Electroblob77 f13f220564 Change tome of arcana description to be more accurate, closes #330. Also adds format args support to multi-line item descriptions and fixes the 1st argument being used instead of the 2nd in the lang files. 2020-01-28 18:36:34 +00:00
Electroblob77 311b5750df Add parrying mechanic to custom fireballs, fixes #335 2020-01-28 18:14:17 +00:00
Electroblob77 e54aee332d Rework slow time to use an NBT tag to unblock entity updates when not in a slow time zone, fixes #336 (and related issues with teleporting)
It also simplifies the code and removes the end slow time packet entirely
2020-01-28 13:20:32 +00:00
Electroblob77 e4bf09f15a Implement client-side spell counter for NPCs, fixes #345
Also makes all the continuous-spell-related methods in ISpellCaster default methods so they don't need implementing unless actually necessary.
2020-01-27 23:27:56 +00:00
Electroblob77 aa166814ea Add SpellThrowable and replace Snowball class with a simple instance of it 2020-01-27 18:40:10 +00:00
Electroblob77 d37ab6072f Add chainable method for assigning spell NPC selector 2020-01-27 17:51:32 +00:00
Electroblob77 ba83cb7985 Add chainable method for assigning spell items 2020-01-27 16:43:05 +00:00
Electroblob77 3052e56d03 Add entity- and tileentity-sensitive versions of Spell#canBeCastByNPCs and Spell#canBeCastByDispensers 2020-01-27 16:42:28 +00:00
Electroblob77 b682740751 *facepalm* 2020-01-27 15:09:54 +00:00
Electroblob77 38429cb697 Update credits 2020-01-27 14:50:55 +00:00
Electroblob77 dc7b6790b8 Automatic spell sound creation, hurrah! 2020-01-27 14:39:47 +00:00
Electroblob77 7a247df3e1 Encourage users to report the circular NBT reference error 2020-01-27 14:33:45 +00:00
Electroblob77 de2087c0e8 Clean up after testing 2020-01-27 13:46:50 +00:00
Electroblob77 f5da195605 Wrap NBTTagCompound#setTag in static helper with safeguards against circular references, fixes #299 2020-01-27 13:45:47 +00:00
Electroblob77 6cd6411c25 Use target lifetime rather than construct lifetime when dealing periodic damage; means targets don't all get damaged at once 2020-01-25 22:50:45 +00:00
Electroblob77 40e77728ad Do proper floaty division, the error was kinda invisible (pun intended) but intellij spotted it 2020-01-21 20:16:59 +00:00
Electroblob77 d50ff5729f Prevent unlimited exponential stacking of the empowerment effect, fixes #317 2020-01-16 18:40:52 +00:00
Electroblob77 76b4f2f72f Change default behaviour of Spell#createSoundWithSuffix to point to assets/modid/sounds.json rather than assets/ebwizardry/sounds.json, so addons don't have to override it 2020-01-14 14:39:09 +00:00
Electroblob77 a608375433 Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2020-01-13 18:59:19 +00:00
Electroblob77 c3f8ba245d Fixing the really important bugs
(What? That pixel was annoying me...)
2020-01-13 18:58:21 +00:00
Electroblob77 34b3fe93bb Bit of an oversight there, this is supposed to be an API after all 2020-01-13 18:55:36 +00:00
Electroblob77 996ec9a7f7 Electroblob hit the ground too hard 2020-01-13 18:54:43 +00:00
Electroblob 2b38ca9a89 Merge pull request #331 from bigenergy/patch-2
Update russian lang
2019-12-31 11:02:52 +00:00
Big Energy 67788a2bd8 Ring translation 2019-12-27 20:23:35 +03:00
Big Energy 58fcaa8af2 Update ru_ru.lang 2019-12-27 20:09:30 +03:00
Big Energy 24e89c8b8a Update ru_ru.lang 2019-12-27 20:01:35 +03:00
Big Energy 1293b89905 Update ru_ru.lang 2019-12-27 19:55:41 +03:00
137 changed files with 7277 additions and 1314 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ assignees: ''
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/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.7 [change as necessary] Wizardry version: 4.2.10 [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
@@ -10,7 +10,7 @@ assignees: ''
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/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.7 [change as necessary] Wizardry version: 4.2.10 [change as necessary]
Question details: [explain your question here] Question details: [explain your question here]
+11 -7
View File
@@ -1,7 +1,7 @@
# Credits # Credits
Electroblob's Wizardry Electroblob's Wizardry
Version 4.2.7 Version 4.2.10
For Minecraft 1.12.2 For Minecraft 1.12.2
Designed, coded and textured by Electroblob Designed, coded and textured by Electroblob
@@ -23,15 +23,19 @@ In addition, I'd like to thank the following individuals for their contributions
- Aeronica - Aeronica
- UltraHex - UltraHex
- Azim-Palmer - Azim-Palmer
- raoulvdberge
#### Translations #### Translations
- Spanish and Mexican Spanish: MadWrist - Spanish: MadWrist, Alsentar
- Russian: VilagVil, kellixon - Mexican Spanish: MadWrist
- French: Hahdrim - Russian: VilagVil, kellixon, bigenergy
- Brazilian Portuguese: lorrampi - French: Hahdrim
- Chinese: ZHENGLOC, dragon-evol - Brazilian Portuguese: lorrampi
- Korean: shejery, rewi_wire - Chinese: ZHENGLOC, dragon-evol, Hokorizero, TUsama
- Korean: shejery, rewi_wire
- Polish: Trozuu - Polish: Trozuu
- German: BirdyDragon
- Taiwanese: chesterccj305
Lightning ray sound effect from OhhWowProductions Lightning ray sound effect from OhhWowProductions
+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.2.7 [change as necessary] Wizardry version: 4.2.10 [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.]
+8 -36
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.7" // There, it matches semver, happy now? version = "4.2.10" // 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"
@@ -31,15 +31,10 @@ repositories {
name = "ModMaven" name = "ModMaven"
url = "modmaven.k-4u.nl" url = "modmaven.k-4u.nl"
} }
flatDir { maven {
// evErYthINg hAS tO Be a mAVeN rEPoSiTOrY... (to use deobfProvided, for some reason) name = "Curseforge Maven"
// The lack of documentation is actually quite impressive, see the following threads: url = "https://minecraft.curseforge.com/api/maven/"
// http://www.minecraftforge.net/forum/topic/44262-solved-how-to-build-mod-with-dependencies/
// https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/mapping-and-modding-tutorials/2866817-setting-up-dependencies-in-minecraft-forge-with
// http://www.minecraftforge.net/forum/topic/44534-adding-dependencies-to-a-forge-project/
dirs "libsfordeobf" // Don't put src jars in libs or forge will think you have duplicate mods
} }
} }
minecraft { minecraft {
@@ -56,37 +51,14 @@ minecraft {
} }
dependencies { dependencies {
// you may put jars on which you depend on in ./libs
// or you may define them like so..
//compile "some.group:artifact:version:classifier"
//compile "some.group:artifact:version"
// compile against the JEI API but do not include it at runtime // Compile against the JEI API but do not include it at runtime
deobfProvided "mezz.jei:jei_${mc_version}:${jei_version}:api" deobfProvided "mezz.jei:jei_${mc_version}:${jei_version}:api"
// at runtime, use the full JEI jar // At runtime, use the full JEI jar
runtime "mezz.jei:jei_${mc_version}:${jei_version}" runtime "mezz.jei:jei_${mc_version}:${jei_version}"
//deobfProvided "baubles:baubles-1.12:1.5.2:api" // #ihavenoideawhatimdoing #itjustworks deobfCompile "baubles:Baubles:${mc_version_short}:${baubles_version}"
//runtime "baubles:baubles-1.12:1.5.2" // If in doubt, copy what already works deobfCompile "antique-atlas:antiqueatlas:${mc_version}:${antique_atlas_version}"
//deobfProvided "antiqueatlas:antiqueatlas-1.12.2:4.5.1:src"
//runtime "antiqueatlas:antiqueatlas-1.12.2:4.5.1"
// real examples
//compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
//compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
// the 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
//provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// the deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided,
// except that these dependencies get remapped to your current MCP mappings
//deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev'
//deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// for more info...
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
} }
+4 -1
View File
@@ -2,4 +2,7 @@
# This is required to provide enough memory for the Minecraft decompilation process. # This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G org.gradle.jvmargs=-Xmx3G
mc_version=1.12.2 mc_version=1.12.2
jei_version=4.13.1.225 mc_version_short=1.12
jei_version=4.13.1.225
baubles_version=1.5.2
antique_atlas_version=4.6.3
@@ -100,9 +100,9 @@ public class CommonProxy {
return ((ItemSpectralBow)WizardryItems.spectral_bow).getDefaultDurabilityForDisplay(stack); return ((ItemSpectralBow)WizardryItems.spectral_bow).getDefaultDurabilityForDisplay(stack);
} }
/** Like {@link CommonProxy#addMultiLineDescription(List, String, Style)}, but style defaults to light grey. */ /** Like {@link CommonProxy#addMultiLineDescription(List, String, Style, Object...)}, but style defaults to light grey. */
public void addMultiLineDescription(List<String> tooltip, String key){ public void addMultiLineDescription(List<String> tooltip, String key, Object... args){
this.addMultiLineDescription(tooltip, key, new Style().setColor(TextFormatting.GRAY)); this.addMultiLineDescription(tooltip, key, new Style().setColor(TextFormatting.GRAY), args);
} }
/** /**
@@ -113,7 +113,7 @@ public class CommonProxy {
* @param key The translation key for the description * @param key The translation key for the description
* @param style A style to apply * @param style A style to apply
*/ */
public void addMultiLineDescription(List<String> tooltip, String key, Style style){} public void addMultiLineDescription(List<String> tooltip, String key, Style style, Object... args){}
// SECTION Packet Handlers // SECTION Packet Handlers
// =============================================================================================================== // ===============================================================================================================
@@ -140,8 +140,6 @@ public class CommonProxy {
public void handleAdvancementSyncPacket(PacketSyncAdvancements.Message message){} public void handleAdvancementSyncPacket(PacketSyncAdvancements.Message message){}
public void handleEndSlowTimePacket(PacketEndSlowTime.Message message){}
public void handleResurrectionPacket(PacketResurrection.Message message){} public void handleResurrectionPacket(PacketResurrection.Message message){}
public void handlePossessionPacket(PacketPossession.Message message){} public void handlePossessionPacket(PacketPossession.Message message){}
@@ -242,6 +240,13 @@ public class CommonProxy {
/** Starts the first-person blink overlay effect for the specified player. */ /** Starts the first-person blink overlay effect for the specified player. */
public void playBlinkEffect(EntityPlayer player){} public void playBlinkEffect(EntityPlayer player){}
/**
* Starts the client-side screen shake effect for the specified player.
* @param player The player whose screen is to be shaken
* @param intensity The amplitude of the shaking (around 10 looks about right)
*/
public void shakeScreen(EntityPlayer player, float intensity){}
/** /**
* 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
@@ -6,8 +6,10 @@ import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.AllyDesignationSystem.FriendlyFire; import electroblob.wizardry.util.AllyDesignationSystem.FriendlyFire;
import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.MagicDamage.DamageType; import electroblob.wizardry.util.MagicDamage.DamageType;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityList;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.translation.I18n; import net.minecraft.util.text.translation.I18n;
import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.ConfigCategory;
@@ -16,6 +18,8 @@ import net.minecraftforge.common.config.Property;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 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 net.minecraftforge.oredict.OreDictionary;
import org.apache.commons.lang3.tuple.Pair;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
@@ -90,7 +94,7 @@ public final class Settings {
/** <b>[Server-only]</b> List of dimension ids in which to generate wizard towers. */ /** <b>[Server-only]</b> List of dimension ids in which to generate wizard towers. */
public int[] towerDimensions = {0}; public int[] towerDimensions = {0};
/** <b>[Server-only]</b> The rarity of wizard towers, used by the world generator. Larger numbers are rarer. */ /** <b>[Server-only]</b> The rarity of wizard towers, used by the world generator. Larger numbers are rarer. */
public int towerRarity = 900; public int towerRarity = 700;
/** <b>[Server-only]</b> List of structure file locations for wizard towers without loot chests. */ /** <b>[Server-only]</b> List of structure file locations for wizard towers without loot chests. */
public ResourceLocation[] towerFiles = {new ResourceLocation(Wizardry.MODID, "wizard_tower_0"), public ResourceLocation[] towerFiles = {new ResourceLocation(Wizardry.MODID, "wizard_tower_0"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_1"), new ResourceLocation(Wizardry.MODID, "wizard_tower_1"),
@@ -104,7 +108,7 @@ public final class Settings {
/** <b>[Server-only]</b> List of dimension ids in which to generate obelisks. */ /** <b>[Server-only]</b> List of dimension ids in which to generate obelisks. */
public int[] obeliskDimensions = {0, -1}; public int[] obeliskDimensions = {0, -1};
/** <b>[Server-only]</b> The rarity of obelisks, used by the world generator. Larger numbers are rarer. */ /** <b>[Server-only]</b> The rarity of obelisks, used by the world generator. Larger numbers are rarer. */
public int obeliskRarity = 800; public int obeliskRarity = 600;
/** <b>[Server-only]</b> List of structure file locations for obelisks. */ /** <b>[Server-only]</b> List of structure file locations for obelisks. */
public ResourceLocation[] obeliskFiles = {new ResourceLocation(Wizardry.MODID, "obelisk_0"), public ResourceLocation[] obeliskFiles = {new ResourceLocation(Wizardry.MODID, "obelisk_0"),
new ResourceLocation(Wizardry.MODID, "obelisk_1"), new ResourceLocation(Wizardry.MODID, "obelisk_1"),
@@ -114,7 +118,7 @@ public final class Settings {
/** <b>[Server-only]</b> List of dimension ids in which to generate shrines. */ /** <b>[Server-only]</b> List of dimension ids in which to generate shrines. */
public int[] shrineDimensions = {0, -1}; public int[] shrineDimensions = {0, -1};
/** <b>[Server-only]</b> The rarity of shrines, used by the world generator. Larger numbers are rarer. */ /** <b>[Server-only]</b> The rarity of shrines, used by the world generator. Larger numbers are rarer. */
public int shrineRarity = 1500; public int shrineRarity = 1100;
/** <b>[Server-only]</b> List of structure file locations for shrines. */ /** <b>[Server-only]</b> List of structure file locations for shrines. */
public ResourceLocation[] shrineFiles = {new ResourceLocation(Wizardry.MODID, "shrine_0"), public ResourceLocation[] shrineFiles = {new ResourceLocation(Wizardry.MODID, "shrine_0"),
new ResourceLocation(Wizardry.MODID, "shrine_1"), new ResourceLocation(Wizardry.MODID, "shrine_1"),
@@ -125,7 +129,7 @@ public final class Settings {
new ResourceLocation(Wizardry.MODID, "shrine_6"), new ResourceLocation(Wizardry.MODID, "shrine_6"),
new ResourceLocation(Wizardry.MODID, "shrine_7")}; new ResourceLocation(Wizardry.MODID, "shrine_7")};
/** <b>[Server-only]</b> List of solid blocks (usually trees) which are ignored by the structure generators. */ /** <b>[Server-only]</b> List of solid blocks (usually trees) which are ignored by the structure generators. */
public ResourceLocation[] treeBlocks = toResourceLocations(DEFAULT_TREE_BLOCKS); public Pair<ResourceLocation, Short>[] treeBlocks = parseItemMetaStrings(DEFAULT_TREE_BLOCKS);
/** <b>[Server-only]</b> The chance for wizard towers to generate with an evil wizard and chest inside. */ /** <b>[Server-only]</b> The chance for wizard towers to generate with an evil wizard and chest inside. */
public double evilWizardChance = 0.2; public double evilWizardChance = 0.2;
/** <b>[Server-only]</b> List of dimension ids in which to generate crystal ore. */ /** <b>[Server-only]</b> List of dimension ids in which to generate crystal ore. */
@@ -164,8 +168,6 @@ public final class Settings {
public boolean playerBlockDamage = true; public boolean playerBlockDamage = true;
/** <b>[Server-only]</b> Whether to revert to the old wand upgrade system, which only requires tomes of arcana. */ /** <b>[Server-only]</b> Whether to revert to the old wand upgrade system, which only requires tomes of arcana. */
public boolean legacyWandLevelling = false; public boolean legacyWandLevelling = false;
/** <b>[Server-only]</b> Whether to replace Minecraft's own fireballs with wizardry fireballs. */
public boolean replaceVanillaFireballs = true;
/** <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;
@@ -190,15 +192,15 @@ public final class Settings {
* <b>[Server-only]</b> List of registry names of items which cannot be smelted by the pocket furnace spell, in * <b>[Server-only]</b> List of registry names of items which cannot be smelted by the pocket furnace spell, in
* addition to armour, tools and weapons. * addition to armour, tools and weapons.
*/ */
public ResourceLocation[] pocketFurnaceItemBlacklist = toResourceLocations("cobblestone", "netherrack"); public Pair<ResourceLocation, Short>[] pocketFurnaceItemBlacklist = parseItemMetaStrings("cobblestone", "netherrack");
/** <b>[Server-only]</b> List of registry names of blocks which can be detected by the divination spell. */ /** <b>[Server-only]</b> List of registry names of blocks which can be detected by the divination spell. */
public ResourceLocation[] divinationOreWhitelist = {}; public Pair<ResourceLocation, Short>[] divinationOreWhitelist = parseItemMetaStrings(); // That works I guess
/** <b>[Server-only]</b> List of registry names of items which count as swords for imbuement spells. */ /** <b>[Server-only]</b> List of registry names of items which count as swords for imbuement spells. */
public ResourceLocation[] swordItemWhitelist = {}; public Pair<ResourceLocation, Short>[] swordItemWhitelist = parseItemMetaStrings();
/** <b>[Server-only]</b> List of registry names of items which count as bows for imbuement spells. */ /** <b>[Server-only]</b> List of registry names of items which count as bows for imbuement spells. */
public ResourceLocation[] bowItemWhitelist = {}; public Pair<ResourceLocation, Short>[] bowItemWhitelist = parseItemMetaStrings();
/** <b>[Server-only]</b> Map of items to values which wizard trades may use as currency. */ /** <b>[Server-only]</b> Map of items to values which wizard trades may use as currency. */
public Map<ResourceLocation, Integer> currencyItems = new HashMap<>(); public Map<Pair<ResourceLocation, Short>, Integer> currencyItems = new HashMap<>();
/** <b>[Server-only]</b> Global damage scaling factor for all player magic damage. */ /** <b>[Server-only]</b> Global damage scaling factor for all player magic damage. */
public double playerDamageScale = 1.0; public double playerDamageScale = 1.0;
/** <b>[Server-only]</b> Global damage scaling factor for all npc magic damage. */ /** <b>[Server-only]</b> Global damage scaling factor for all npc magic damage. */
@@ -268,7 +270,9 @@ public final class Settings {
* effect. * effect.
*/ */
public boolean slowTimeAffectsPlayers = true; public boolean slowTimeAffectsPlayers = true;
/** <b>[Synchronised]</b> Chance of 'misreading' an undiscovered spell and triggering a forfeit instead. */ /** <b>[Synchronised]</b> Whether to replace Minecraft's own fireballs with wizardry fireballs. */
public boolean replaceVanillaFireballs = true;
/** <b>[Synchronised]</b> Chance of 'misreading' an undiscovered spell and triggering a forfeit instead. */
public double forfeitChance = 0.2; public double forfeitChance = 0.2;
// Client-only settings. These settings only affect client-side code and hence are not synced. Each client obeys // Client-only settings. These settings only affect client-side code and hence are not synced. Each client obeys
@@ -433,7 +437,7 @@ public final class Settings {
Property property; Property property;
for(Spell spell : Spell.getSpells(Spell.allSpells)){ for(Spell spell : Spell.getAllSpells()){
property = config.get(SPELLS_CATEGORY, spell.getRegistryName().toString(), true, property = config.get(SPELLS_CATEGORY, spell.getRegistryName().toString(), true,
I18n.translateToLocal("spell." + spell.getUnlocalisedName() + ".desc")); I18n.translateToLocal("spell." + spell.getUnlocalisedName() + ".desc"));
// Uses the same config key as the spell name, because - well, that's what it's called! // Uses the same config key as the spell name, because - well, that's what it's called!
@@ -652,41 +656,42 @@ public final class Settings {
"List of registry names of blocks or items which cannot be smelted by the pocket furnace spell, in addition to armour, tools and weapons. Block/item names are not case sensitive. For mod items, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore)."); "List of registry names of blocks or items which cannot be smelted by the pocket furnace spell, in addition to armour, tools and weapons. Block/item names are not case sensitive. For mod items, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore).");
property.setLanguageKey("config." + Wizardry.MODID + ".pocket_furnace_item_blacklist"); property.setLanguageKey("config." + Wizardry.MODID + ".pocket_furnace_item_blacklist");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
pocketFurnaceItemBlacklist = getResourceLocationList(property); pocketFurnaceItemBlacklist = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "divinationOreWhitelist", new String[0], "List of registry names of ore blocks which can be detected by the divination spell. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore)."); property = config.get(GAMEPLAY_CATEGORY, "divinationOreWhitelist", new String[0], "List of registry names of ore blocks which can be detected by the divination spell. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore).");
property.setLanguageKey("config." + Wizardry.MODID + ".divination_ore_whitelist"); property.setLanguageKey("config." + Wizardry.MODID + ".divination_ore_whitelist");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
divinationOreWhitelist = getResourceLocationList(property); divinationOreWhitelist = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "swordItemWhitelist", new String[0], "List of registry names of items which should count as swords for imbuement spells. Most swords should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:broadsword)."); property = config.get(GAMEPLAY_CATEGORY, "swordItemWhitelist", new String[0], "List of registry names of items which should count as swords for imbuement spells. Most swords should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:broadsword).");
property.setLanguageKey("config." + Wizardry.MODID + ".sword_item_whitelist"); property.setLanguageKey("config." + Wizardry.MODID + ".sword_item_whitelist");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
swordItemWhitelist = getResourceLocationList(property); swordItemWhitelist = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "bowItemWhitelist", new String[0], "List of registry names of items which should count as bows for imbuement spells. Most bows should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:shortbow)."); property = config.get(GAMEPLAY_CATEGORY, "bowItemWhitelist", new String[0], "List of registry names of items which should count as bows for imbuement spells. Most bows should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:shortbow).");
property.setLanguageKey("config." + Wizardry.MODID + ".bow_item_whitelist"); property.setLanguageKey("config." + Wizardry.MODID + ".bow_item_whitelist");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
bowItemWhitelist = getResourceLocationList(property); bowItemWhitelist = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade.", property = config.get(GAMEPLAY_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade.",
Pattern.compile("[A-Za-z:_]+ [0-9]+")); Pattern.compile("[A-Za-z0-9:_]+ [0-9]+"));
property.setLanguageKey("config." + Wizardry.MODID + ".currency_items"); property.setLanguageKey("config." + Wizardry.MODID + ".currency_items");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
propOrder.add(property.getName()); propOrder.add(property.getName());
currencyItems = new HashMap<>(); currencyItems = new HashMap<>();
for(String string : property.getStringList()){ for(String string : property.getStringList()){
string = string.toLowerCase(Locale.ROOT).trim();
String[] args = string.split(" "); String[] args = string.split(" ");
if(args.length != 2){ if(args.length != 2){
Wizardry.logger.warn("Invalid entry in currency items: {}", string); Wizardry.logger.warn("Invalid entry in currency items: {}", string);
continue; // Ignore invalid entries, the pattern above should ensure this never happens continue; // Ignore invalid entries, the pattern above should ensure this never happens
} }
try { try {
currencyItems.put(new ResourceLocation(args[0]), Integer.parseInt(args[1])); currencyItems.put(parseItemMetaString(args[0]), Integer.parseInt(args[1]));
}catch(NumberFormatException e){ }catch(NumberFormatException e){
Wizardry.logger.warn("Invalid integer in currency items: {}", args[1]); Wizardry.logger.warn("Invalid integer in currency items: {}", args[1]);
} }
@@ -791,7 +796,7 @@ public final class Settings {
property = config.get(GAMEPLAY_CATEGORY, "treeBlocks", DEFAULT_TREE_BLOCKS, "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)."); property = config.get(GAMEPLAY_CATEGORY, "treeBlocks", DEFAULT_TREE_BLOCKS, "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).");
property.setLanguageKey("config." + Wizardry.MODID + ".tree_blocks"); property.setLanguageKey("config." + Wizardry.MODID + ".tree_blocks");
property.setRequiresWorldRestart(true); property.setRequiresWorldRestart(true);
treeBlocks = getResourceLocationList(property); treeBlocks = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "oreDimensions", new int[]{0}, "List of dimension ids in which crystal ore will generate. Note that removing the overworld (id 0) from this list will make the mod VERY difficult to play!"); property = config.get(WORLDGEN_CATEGORY, "oreDimensions", new int[]{0}, "List of dimension ids in which crystal ore will generate. Note that removing the overworld (id 0) from this list will make the mod VERY difficult to play!");
@@ -1082,4 +1087,45 @@ public final class Settings {
public static ResourceLocation[] toResourceLocations(String... strings){ public static ResourceLocation[] toResourceLocations(String... strings){
return Arrays.stream(strings).map(s -> new ResourceLocation(s.toLowerCase(Locale.ROOT).trim())).toArray(ResourceLocation[]::new); return Arrays.stream(strings).map(s -> new ResourceLocation(s.toLowerCase(Locale.ROOT).trim())).toArray(ResourceLocation[]::new);
} }
/** Applies {@link Settings#parseItemMetaString(String)} to each input string and returns and array of the resulting
* {@link Pair}s. */
@SuppressWarnings("unchecked") // Shut up java
public static Pair<ResourceLocation, Short>[] parseItemMetaStrings(String... strings){
return Arrays.stream(strings).map(Settings::parseItemMetaString).toArray(Pair[]::new);
}
/** Parses the given input string as an item of the form {@code id:metadata} and returns the resulting
* {@link ResourceLocation} ID and metadata value as a {@link Pair} object. */
public static Pair<ResourceLocation, Short> parseItemMetaString(String string){
string = string.toLowerCase(Locale.ROOT).trim();
String[] itemArgs = string.split(":");
String item;
short meta;
try {
meta = Short.parseShort(itemArgs[itemArgs.length-1]);
item = String.join(":", Arrays.copyOfRange(itemArgs, 0, itemArgs.length-1));
}catch(NumberFormatException e){ // If no metadata is specified
meta = OreDictionary.WILDCARD_VALUE;
item = string;
}
return Pair.of(new ResourceLocation(item), meta);
}
public static boolean containsMetaBlock(Pair<ResourceLocation, Short>[] array, IBlockState block){
return containsMetaThing(array, block.getBlock().getRegistryName(), (short)block.getBlock().getMetaFromState(block));
}
public static boolean containsMetaItem(Pair<ResourceLocation, Short>[] array, ItemStack stack){
return containsMetaThing(array, stack.getItem().getRegistryName(), (short)stack.getMetadata());
}
public static boolean containsMetaThing(Pair<ResourceLocation, Short>[] array, ResourceLocation id, short metadata){
return Arrays.asList(array).contains(Pair.of(id, metadata)) || Arrays.asList(array).contains(Pair.of(id, OreDictionary.WILDCARD_VALUE));
}
} }
@@ -46,7 +46,7 @@ import java.util.Calendar;
*/ */
@Mod(modid = Wizardry.MODID, name = Wizardry.NAME, version = Wizardry.VERSION, acceptedMinecraftVersions = "[1.12.2]", @Mod(modid = Wizardry.MODID, name = Wizardry.NAME, version = Wizardry.VERSION, acceptedMinecraftVersions = "[1.12.2]",
guiFactory = "electroblob.wizardry.WizardryGuiFactory", dependencies = "required-after:forge@[14.23.5.2814,)") guiFactory = "electroblob.wizardry.WizardryGuiFactory", dependencies = "required-after:forge@[14.23.5.2814,);after:antiqueatlas@[4.6,)")
public class Wizardry { public class Wizardry {
@@ -64,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.7"; public static final String VERSION = "4.2.10";
// 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
@@ -290,7 +290,7 @@ public final class WizardryEventHandler {
EntityLivingBase attacker = (EntityLivingBase)event.getSource().getTrueSource(); EntityLivingBase attacker = (EntityLivingBase)event.getSource().getTrueSource();
// Players can only ever attack with their main hand, so this is the right method to use here. // Players can only ever attack with their main hand, so this is the right method to use here.
if(!attacker.getHeldItemMainhand().isEmpty() && ImbueWeapon.isSword(attacker.getHeldItemMainhand().getItem())){ if(!attacker.getHeldItemMainhand().isEmpty() && ImbueWeapon.isSword(attacker.getHeldItemMainhand())){
int level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.flaming_weapon, int level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.flaming_weapon,
attacker.getHeldItemMainhand()); attacker.getHeldItemMainhand());
@@ -347,18 +347,21 @@ public final class WizardryEventHandler {
Spell spell = ((ISpellCaster)event.getEntity()).getContinuousSpell(); Spell spell = ((ISpellCaster)event.getEntity()).getContinuousSpell();
SpellModifiers modifiers = ((ISpellCaster)event.getEntity()).getModifiers(); SpellModifiers modifiers = ((ISpellCaster)event.getEntity()).getModifiers();
int count = ((ISpellCaster)event.getEntity()).getSpellCounter();
if(spell != null && spell != Spells.none){ // IntelliJ is wrong, do NOT remove the null check! if(spell != null && spell != Spells.none){ // IntelliJ is wrong, do NOT remove the null check!
if(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(SpellCastEvent.Source.NPC, spell, event.getEntityLiving(), if(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(SpellCastEvent.Source.NPC, spell, event.getEntityLiving(),
modifiers, 0))){ modifiers, count))){
spell.cast(event.getEntity().world, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, 0, spell.cast(event.getEntity().world, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, count,
// TODO: This implementation of modifiers relies on them being accessible client-side. // TODO: This implementation of modifiers relies on them being accessible client-side.
// Right now that doesn't matter because NPCs don't use modifiers, but they might in future // Right now that doesn't matter because NPCs don't use modifiers, but they might in future
((EntityLiving)event.getEntity()).getAttackTarget(), modifiers); ((EntityLiving)event.getEntity()).getAttackTarget(), modifiers);
} }
} }
((ISpellCaster)event.getEntity()).setSpellCounter(count + 1);
} }
} }
} }
@@ -417,7 +420,7 @@ public final class WizardryEventHandler {
@SubscribeEvent // Priority doesn't matter here, we're only setting event fields so if it's cancelled it won't matter @SubscribeEvent // Priority doesn't matter here, we're only setting event fields so if it's cancelled it won't matter
public static void onLivingFallEvent(LivingFallEvent event){ public static void onLivingFallEvent(LivingFallEvent event){
// Why is fall damage based on distance fallen? Why? Who on earth came up with that? It makes no sense whatsoever! // Why is fall damage based on distance fallen? Why? Who on earth came up with that? It makes no sense whatsoever!
if(Wizardry.settings.replaceVanillaFallDamage && !Loader.isModLoaded("speedbasedfalldamage")){ if(!event.getEntity().world.isRemote && Wizardry.settings.replaceVanillaFallDamage && !Loader.isModLoaded("speedbasedfalldamage")){
// We want to keep the fall damage EXACTLY THE SAME for free, uninterrupted falls, but fix the weirdness // We want to keep the fall damage EXACTLY THE SAME for free, uninterrupted falls, but fix the weirdness
// caused when something else changes the entity's velocity // caused when something else changes the entity's velocity
// All living entities have a gravity of 0.08b/t^2 // All living entities have a gravity of 0.08b/t^2
@@ -464,7 +467,8 @@ public final class WizardryEventHandler {
// Wizardry.logger.info("Replaced fall distance {} with effective distance {} based on entity velocity", event.getDistance(), y); // Wizardry.logger.info("Replaced fall distance {} with effective distance {} based on entity velocity", event.getDistance(), y);
// } // }
event.setDistance((float)y); // Don't let it increase fall damage beyond vanilla values
if(y < event.getDistance()) event.setDistance((float)y);
} }
} }
} }
@@ -18,6 +18,7 @@ import net.minecraft.item.Item;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer; import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumBlockRenderType; import net.minecraft.util.EnumBlockRenderType;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
@@ -54,21 +55,24 @@ public class BlockSnare extends Block implements ITileEntityProvider {
@Override @Override
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){ public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
if(!world.isRemote && entity instanceof EntityLivingBase){ if(entity instanceof EntityLivingBase){
if(world.getTileEntity(pos) instanceof TileEntityPlayerSave){ if(world.getTileEntity(pos) instanceof TileEntityPlayerSave){
TileEntityPlayerSave tileentity = (TileEntityPlayerSave)world.getTileEntity(pos); TileEntityPlayerSave tileentity = (TileEntityPlayerSave)world.getTileEntity(pos);
if(AllyDesignationSystem.isValidTarget(tileentity.getCaster(), entity)){ if(AllyDesignationSystem.isValidTarget(tileentity.getCaster(), entity)){
entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC), DamageSource source = tileentity.getCaster() == null ? DamageSource.CACTUS
Spells.snare.getProperty(Spell.DAMAGE).floatValue()); : MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC);
entity.attackEntityFrom(source, Spells.snare.getProperty(Spell.DAMAGE).floatValue());
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS,
Spells.snare.getProperty(Spell.EFFECT_DURATION).intValue(), Spells.snare.getProperty(Spell.EFFECT_DURATION).intValue(),
Spells.snare.getProperty(Spell.EFFECT_STRENGTH).intValue())); Spells.snare.getProperty(Spell.EFFECT_STRENGTH).intValue()));
world.destroyBlock(pos, false); if(!world.isRemote) world.destroyBlock(pos, false);
} }
} }
} }
@@ -137,9 +137,11 @@ public class BlockThorns extends BlockBush implements ITileEntityProvider {
TileEntity tileentity = world.getTileEntity(pos); TileEntity tileentity = world.getTileEntity(pos);
if(tileentity instanceof TileEntityPlayerSaveTimed){ if(tileentity instanceof TileEntityPlayerSaveTimed){
if(AllyDesignationSystem.isValidTarget(((TileEntityPlayerSaveTimed)tileentity).getCaster(), target)){
source = MagicDamage.causeDirectMagicDamage(((TileEntityPlayerSaveTimed)tileentity).getCaster(), EntityLivingBase caster = ((TileEntityPlayerSaveTimed)tileentity).getCaster();
MagicDamage.DamageType.MAGIC);
if(caster != null && AllyDesignationSystem.isValidTarget(caster, target)){
source = MagicDamage.causeDirectMagicDamage(caster, MagicDamage.DamageType.MAGIC);
}else{ }else{
return false; // Don't attack or slow allies of the caster return false; // Don't attack or slow allies of the caster
} }
@@ -29,7 +29,6 @@ import electroblob.wizardry.item.ItemScroll;
import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.packet.*; import electroblob.wizardry.packet.*;
import electroblob.wizardry.potion.PotionSlowTime;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.*; import electroblob.wizardry.spell.*;
@@ -107,7 +106,7 @@ public class ClientProxy extends CommonProxy {
// Armour Model // Armour Model
public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f); public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f);
/** The wrap width for standard multi-line descriptions (see {@link ClientProxy#addMultiLineDescription(List, String, Style)}). */ /** The wrap width for standard multi-line descriptions (see {@link ClientProxy#addMultiLineDescription(List, String, Style, Object...)}). */
private static final int TOOLTIP_WRAP_WIDTH = 140; private static final int TOOLTIP_WRAP_WIDTH = 140;
// SECTION Registry // SECTION Registry
@@ -195,6 +194,11 @@ public class ClientProxy extends CommonProxy {
if(Minecraft.getMinecraft().player == player) WizardryClientEventHandler.playBlinkEffect(); if(Minecraft.getMinecraft().player == player) WizardryClientEventHandler.playBlinkEffect();
} }
@Override
public void shakeScreen(EntityPlayer player, float intensity){
if(Minecraft.getMinecraft().player == player) WizardryClientEventHandler.shakeScreen(intensity);
}
@Override @Override
public Set<String> getSpellHUDSkins(){ public Set<String> getSpellHUDSkins(){
return GuiSpellDisplay.getSkinKeys(); return GuiSpellDisplay.getSkinKeys();
@@ -287,8 +291,8 @@ public class ClientProxy extends CommonProxy {
} }
@Override @Override
public void addMultiLineDescription(List<String> tooltip, String key, Style style){ public void addMultiLineDescription(List<String> tooltip, String key, Style style, Object... args){
String description = style.getFormattingCode() + I18n.format(key); String description = style.getFormattingCode() + I18n.format(key, args);
tooltip.addAll(Minecraft.getMinecraft().fontRenderer.listFormattedStringToWidth(description, TOOLTIP_WRAP_WIDTH)); tooltip.addAll(Minecraft.getMinecraft().fontRenderer.listFormattedStringToWidth(description, TOOLTIP_WRAP_WIDTH));
} }
@@ -434,6 +438,7 @@ public class ClientProxy extends CommonProxy {
if(caster instanceof ISpellCaster){ if(caster instanceof ISpellCaster){
if(spell.isContinuous || spell instanceof None){ if(spell.isContinuous || spell instanceof None){
((ISpellCaster)caster).setContinuousSpell(spell); ((ISpellCaster)caster).setContinuousSpell(spell);
((ISpellCaster)caster).setSpellCounter(spell instanceof None ? 0 : 1);
((EntityLiving)caster).setAttackTarget((EntityLivingBase)target); ((EntityLiving)caster).setAttackTarget((EntityLivingBase)target);
} }
} }
@@ -547,7 +552,7 @@ public class ClientProxy extends CommonProxy {
data.randomNames = new HashMap<>(); data.randomNames = new HashMap<>();
data.randomDescriptions = new HashMap<>(); data.randomDescriptions = new HashMap<>();
for(Spell spell : Spell.getSpells(Spell.allSpells)){ for(Spell spell : Spell.getAllSpells()){
// -1 because the none spell isn't included // -1 because the none spell isn't included
// This is a case where we must use the network ID, not the metadata // This is a case where we must use the network ID, not the metadata
data.randomNames.put(spell, message.names.get(spell.networkID() - 1)); data.randomNames.put(spell, message.names.get(spell.networkID() - 1));
@@ -574,13 +579,6 @@ public class ClientProxy extends CommonProxy {
GuiWizardHandbook.updateUnlockStatus(message.showToasts, message.completedAdvancements); GuiWizardHandbook.updateUnlockStatus(message.showToasts, message.completedAdvancements);
} }
@Override
public void handleEndSlowTimePacket(PacketEndSlowTime.Message message){
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.hostID);
if(entity instanceof EntityLivingBase) PotionSlowTime.unblockNearbyEntities((EntityLivingBase)entity);
else Wizardry.logger.warn("Received a PacketEndSlowTime, but the entity ID did not match any living entity");
}
@Override @Override
public void handleResurrectionPacket(PacketResurrection.Message message){ public void handleResurrectionPacket(PacketResurrection.Message message){
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.playerID); Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.playerID);
@@ -9,6 +9,7 @@ import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.ISpellCastingItem; import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.item.ItemArtefact; import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.item.ItemSpectralBow; import electroblob.wizardry.item.ItemSpectralBow;
import electroblob.wizardry.potion.PotionSlowTime;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.registry.WizardryPotions;
@@ -67,19 +68,35 @@ 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;
/** The remaining time for which the screen shake effect will be active. */
private static int screenShakeCounter = 0;
private static final float SHAKINESS = 0.5f;
/** Starts the first person blink overlay effect. */ /** Starts the first-person blink overlay effect. */
public static void playBlinkEffect(){ public static void playBlinkEffect(){
blinkEffectTimer = BLINK_EFFECT_DURATION; blinkEffectTimer = BLINK_EFFECT_DURATION;
} }
/** Starts the client-side screen shake effect. */
public static void shakeScreen(float intensity){
screenShakeCounter = (int)(intensity / SHAKINESS);
Minecraft.getMinecraft().player.rotationPitch -= intensity * 0.5f; // Start halfway down
}
@SubscribeEvent @SubscribeEvent
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){ public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
if(event.player == Minecraft.getMinecraft().player){ if(event.player == Minecraft.getMinecraft().player && event.phase == TickEvent.Phase.END){
if(blinkEffectTimer > 0) blinkEffectTimer--; if(blinkEffectTimer > 0) blinkEffectTimer--;
if(screenShakeCounter > 0){
float magnitude = screenShakeCounter * SHAKINESS;
Minecraft.getMinecraft().player.rotationPitch += screenShakeCounter % 2 == 0 ? magnitude : -magnitude;
screenShakeCounter--;
}
// Only seems to work here... // Only seems to work here...
// EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player); // EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
// if(victim != null && victim.getHeldItemMainhand().isEmpty()){ // if(victim != null && victim.getHeldItemMainhand().isEmpty()){
@@ -153,6 +170,7 @@ public final class WizardryClientEventHandler {
} }
SpellEmitterData.update(world); SpellEmitterData.update(world);
PotionSlowTime.cleanUpEntities(world);
} }
} }
@@ -31,7 +31,6 @@ 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; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@@ -13,6 +13,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord; 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.client.resources.I18n;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
@@ -32,9 +33,7 @@ public class GuiSpellBook extends GuiScreen {
this.spell = Spell.byMetadata(stack.getItemDamage()); this.spell = Spell.byMetadata(stack.getItemDamage());
} }
/** @Override
* Draws the screen and all the components in it.
*/
public void drawScreen(int par1, int par2, float par3){ public void drawScreen(int par1, int par2, float par3){
int xPos = this.width / 2 - xSize / 2; int xPos = this.width / 2 - xSize / 2;
@@ -69,22 +68,18 @@ public class GuiSpellBook extends GuiScreen {
0x777777); 0x777777);
} }
//this.fontRenderer.drawString("-------------------", xPos + 17, yPos + 35, 0); // Novice is usually white but this doesn't show up
String tier = I18n.format("gui.ebwizardry:spell_book.tier", spell.getTier() == Tier.NOVICE ?
"\u00A77" + spell.getTier().getDisplayName() : spell.getTier().getDisplayNameWithFormatting());
this.fontRenderer.drawString(tier, xPos + 17, yPos + 45, 0);
if(spell.getTier() == Tier.NOVICE){ String element = I18n.format("gui.ebwizardry:spell_book.element", spell.getElement().getFormattingCode() + spell.getElement().getDisplayName());
// Novice is usually white but this doesn't show up. if(!discovered) element = I18n.format("gui.ebwizardry:spell_book.element_undiscovered");
this.fontRenderer.drawString("Tier: \u00A77" + Tier.NOVICE.getDisplayName(), xPos + 17, yPos + 45, 0);
}else{
this.fontRenderer.drawString("Tier: " + spell.getTier().getDisplayNameWithFormatting(), xPos + 17, yPos + 45, 0);
}
String element = "Element: " + spell.getElement().getFormattingCode() + spell.getElement().getDisplayName();
if(!discovered) element = "Element: ?";
this.fontRenderer.drawString(element, xPos + 17, yPos + 57, 0); this.fontRenderer.drawString(element, xPos + 17, yPos + 57, 0);
String manaCost = "Mana Cost: " + spell.getCost(); String manaCost = I18n.format("gui.ebwizardry:spell_book.mana_cost", spell.getCost());
if(spell.isContinuous) manaCost = "Mana Cost: " + spell.getCost() + "/second"; if(spell.isContinuous) manaCost = I18n.format("gui.ebwizardry:spell_book.mana_cost_continuous", spell.getCost());
if(!discovered) manaCost = "Mana Cost: ?"; if(!discovered) manaCost = I18n.format("gui.ebwizardry:spell_book.mana_cost_undiscovered");
this.fontRenderer.drawString(manaCost, xPos + 17, yPos + 69, 0); this.fontRenderer.drawString(manaCost, xPos + 17, yPos + 69, 0);
if(discovered){ if(discovered){
@@ -95,6 +90,7 @@ public class GuiSpellBook extends GuiScreen {
} }
} }
@Override
public void initGui(){ public void initGui(){
super.initGui(); super.initGui();
Keyboard.enableRepeatEvents(true); Keyboard.enableRepeatEvents(true);
@@ -103,6 +99,7 @@ public class GuiSpellBook extends GuiScreen {
this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1)); this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1));
} }
@Override
public void onGuiClosed(){ public void onGuiClosed(){
super.onGuiClosed(); super.onGuiClosed();
Keyboard.enableRepeatEvents(false); Keyboard.enableRepeatEvents(false);
@@ -525,30 +525,42 @@ public abstract class ParticleWizardry extends Particle {
// =============================================== Helper Methods =============================================== // =============================================== Helper Methods ===============================================
/** Internal overload for {@link ParticleWizardry#generateTextures(String, String, int)} which uses wizardry's mod
* ID automatically. */
public static ResourceLocation[] generateTextures(String stem, int n){
return generateTextures(Wizardry.MODID, stem, n);
}
/** Static helper method that generates an array of n ResourceLocations using the particle file naming convention, /** Static helper method that generates an array of n ResourceLocations using the particle file naming convention,
* which is the given stem plus an underscore plus the integer index. */ * which is the given stem plus an underscore plus the integer index. */
public static ResourceLocation[] generateTextures(String stem, int n){ public static ResourceLocation[] generateTextures(String modID, String stem, int n){
ResourceLocation[] textures = new ResourceLocation[n]; ResourceLocation[] textures = new ResourceLocation[n];
for(int i=0; i<n; i++){ for(int i=0; i<n; i++){
textures[i] = new ResourceLocation(Wizardry.MODID, "particle/" + stem + "_" + i); textures[i] = new ResourceLocation(modID, "particle/" + stem + "_" + i);
} }
return textures; return textures;
} }
/** Internal overload for {@link ParticleWizardry#generateTextures(String, String, int, int)} which uses wizardry's
* mod ID automatically. */
public static ResourceLocation[][] generateTextures(String stem, int m, int n){
return generateTextures(Wizardry.MODID, stem, m, n);
}
/** Static helper method that generates a 2D m x n array of ResourceLocations using the particle file naming /** Static helper method that generates a 2D m x n array of ResourceLocations using the particle file naming
* convention, which is the given stem plus an underscore plus the first index, plus an underscore plus the second * convention, which is the given stem plus an underscore plus the first index, plus an underscore plus the second
* index. Useful for animated particles that also pick a random animation strip. */ * index. Useful for animated particles that also pick a random animation strip. */
public static ResourceLocation[][] generateTextures(String stem, int m, int n){ public static ResourceLocation[][] generateTextures(String modID, String stem, int m, int n){
ResourceLocation[][] textures = new ResourceLocation[m][n]; ResourceLocation[][] textures = new ResourceLocation[m][n];
for(int i=0; i<m; i++){ for(int i=0; i<m; i++){
for(int j=0; j<n; j++){ for(int j=0; j<n; j++){
textures[i][j] = new ResourceLocation(Wizardry.MODID, "particle/" + stem + "_" + i + "_" + j); textures[i][j] = new ResourceLocation(modID, "particle/" + stem + "_" + i + "_" + j);
} }
} }
@@ -190,7 +190,8 @@ public class CommandCastSpell extends CommandBase {
} }
if(spell.isContinuous){ if(spell.isContinuous){
// We need not query Spell#canBeCastByDispensers since with commands there's no difference between
// a spell that can't be cast positionally and one that can be cast positionally but fails
if(spell.cast(world, origin.x, origin.y, origin.z, direction, 0, duration, modifiers)){ if(spell.cast(world, origin.x, origin.y, origin.z, direction, 0, duration, modifiers)){
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.COMMAND, spell, world, origin.x, origin.y, origin.z, direction, modifiers)); MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.COMMAND, spell, world, origin.x, origin.y, origin.z, direction, modifiers));
@@ -113,7 +113,7 @@ public class CommandDiscoverSpell extends CommandBase {
if(server.sendCommandFeedback()) sender.sendMessage( if(server.sendCommandFeedback()) sender.sendMessage(
new TextComponentTranslation("commands." + Wizardry.MODID + ":discoverspell.clear", player.getName())); new TextComponentTranslation("commands." + Wizardry.MODID + ":discoverspell.clear", player.getName()));
}else if(all){ }else if(all){
data.spellsDiscovered.addAll(Spell.getSpells(Spell.allSpells)); data.spellsDiscovered.addAll(Spell.getAllSpells());
if(server.sendCommandFeedback()) sender.sendMessage( if(server.sendCommandFeedback()) sender.sendMessage(
new TextComponentTranslation("commands." + Wizardry.MODID + ":discoverspell.all", player.getName())); new TextComponentTranslation("commands." + Wizardry.MODID + ":discoverspell.all", player.getName()));
}else{ }else{
@@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry;
import electroblob.wizardry.data.SpellEmitterData; import electroblob.wizardry.data.SpellEmitterData;
import electroblob.wizardry.event.SpellCastEvent; import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.NBTExtras;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
@@ -129,7 +130,7 @@ public class SpellEmitter implements ITickable {
nbt.setDouble("z", z); nbt.setDouble("z", z);
nbt.setInteger("direction", direction.getIndex()); nbt.setInteger("direction", direction.getIndex());
nbt.setInteger("duration", duration); nbt.setInteger("duration", duration);
nbt.setTag("modifiers", modifiers.toNBT()); NBTExtras.storeTagSafely(nbt, "modifiers", modifiers.toNBT());
nbt.setInteger("castingTick", castingTick); nbt.setInteger("castingTick", castingTick);
return nbt; return nbt;
@@ -7,6 +7,7 @@ import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.None; import electroblob.wizardry.spell.None;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.NBTExtras;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
@@ -141,7 +142,7 @@ public abstract class BlockCastingData<T extends TileEntity> implements INBTSeri
nbt.setInteger("spell", spell.metadata()); nbt.setInteger("spell", spell.metadata());
nbt.setInteger("castingTick", castingTick); nbt.setInteger("castingTick", castingTick);
nbt.setTag("modifiers", modifiers.toNBT()); NBTExtras.storeTagSafely(nbt, "modifiers", modifiers.toNBT());
return nbt; return nbt;
} }
@@ -1,5 +1,6 @@
package electroblob.wizardry.data; package electroblob.wizardry.data;
import electroblob.wizardry.util.NBTExtras;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@@ -89,7 +90,7 @@ public interface IStoredVariable<T> extends IVariable<T> {
@Override @Override
public void write(NBTTagCompound nbt, T value){ public void write(NBTTagCompound nbt, T value){
if(value != null) nbt.setTag(key, serialiser.apply(value)); if(value != null) NBTExtras.storeTagSafely(nbt, key, serialiser.apply(value));
} }
@Override @Override
@@ -91,7 +91,7 @@ public class SpellEmitterData extends WorldSavedData {
@Override @Override
public NBTTagCompound writeToNBT(NBTTagCompound compound){ public NBTTagCompound writeToNBT(NBTTagCompound compound){
compound.setTag("emitters", NBTExtras.listToNBT(emitters, SpellEmitter::toNBT)); NBTExtras.storeTagSafely(compound, "emitters", NBTExtras.listToNBT(emitters, SpellEmitter::toNBT));
return compound; return compound;
} }
@@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry;
import electroblob.wizardry.packet.PacketGlyphData; import electroblob.wizardry.packet.PacketGlyphData;
import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.NBTExtras;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagList;
@@ -43,11 +44,11 @@ public class SpellGlyphData extends WorldSavedData {
/** Generates random names and descriptions for any spells which don't already have them. */ /** Generates random names and descriptions for any spells which don't already have them. */
public void generateGlyphNames(World world){ public void generateGlyphNames(World world){
for(Spell spell : Spell.getSpells(Spell.allSpells)){ for(Spell spell : Spell.getAllSpells()){
if(!randomNames.containsKey(spell)) randomNames.put(spell, generateRandomName(world.rand)); if(!randomNames.containsKey(spell)) randomNames.put(spell, generateRandomName(world.rand));
} }
for(Spell spell : Spell.getSpells(Spell.allSpells)){ for(Spell spell : Spell.getAllSpells()){
if(!randomDescriptions.containsKey(spell)) if(!randomDescriptions.containsKey(spell))
randomDescriptions.put(spell, generateRandomDescription(world.rand)); randomDescriptions.put(spell, generateRandomDescription(world.rand));
} }
@@ -161,7 +162,7 @@ public class SpellGlyphData extends WorldSavedData {
NBTTagList tagList = new NBTTagList(); NBTTagList tagList = new NBTTagList();
for(Spell spell : Spell.getSpells(Spell.allSpells)){ for(Spell spell : Spell.getAllSpells()){
// Much like the enchantments tag for items, this stores a list of spell-id-to-name tag pairs // Much like the enchantments tag for items, this stores a list of spell-id-to-name tag pairs
// The description is now also included; there's no point in making a second compound tag! // The description is now also included; there's no point in making a second compound tag!
NBTTagCompound tag = new NBTTagCompound(); NBTTagCompound tag = new NBTTagCompound();
@@ -171,7 +172,7 @@ public class SpellGlyphData extends WorldSavedData {
tagList.appendTag(tag); tagList.appendTag(tag);
} }
nbt.setTag("spellGlyphData", tagList); NBTExtras.storeTagSafely(nbt, "spellGlyphData", tagList);
return nbt; return nbt;
} }
@@ -515,12 +515,12 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
NBTTagCompound properties = new NBTTagCompound(); NBTTagCompound properties = new NBTTagCompound();
properties.setTag("imbuements", NBTExtras.mapToNBT(this.imbuementDurations, NBTExtras.storeTagSafely(properties, "imbuements", NBTExtras.mapToNBT(this.imbuementDurations,
imbuement -> new NBTTagInt(Enchantment.getEnchantmentID((Enchantment)imbuement)), NBTTagInt::new)); imbuement -> new NBTTagInt(Enchantment.getEnchantmentID((Enchantment)imbuement)), NBTTagInt::new));
// Mmmmmm Java 8.... // Mmmmmm Java 8....
properties.setTag("allies", NBTExtras.listToNBT(this.allies, NBTUtil::createUUIDTag)); NBTExtras.storeTagSafely(properties, "allies", NBTExtras.listToNBT(this.allies, NBTUtil::createUUIDTag));
properties.setTag("allyNames", NBTExtras.listToNBT(this.allyNames, NBTTagString::new)); NBTExtras.storeTagSafely(properties, "allyNames", NBTExtras.listToNBT(this.allyNames, NBTTagString::new));
// Might be worth converting this over to WizardryUtilities.listToNBT. // Might be worth converting this over to WizardryUtilities.listToNBT.
int[] spells = new int[this.spellsDiscovered.size()]; int[] spells = new int[this.spellsDiscovered.size()];
@@ -531,7 +531,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
} }
properties.setIntArray("discoveredSpells", spells); properties.setIntArray("discoveredSpells", spells);
properties.setTag("recentSpells", NBTExtras.listToNBT(recentSpells, s -> new NBTTagInt(s.metadata()))); NBTExtras.storeTagSafely(properties, "recentSpells", NBTExtras.listToNBT(recentSpells, s -> new NBTTagInt(s.metadata())));
storedVariables.forEach(k -> k.write(properties, this.spellData.get(k))); storedVariables.forEach(k -> k.write(properties, this.spellData.get(k)));
@@ -68,7 +68,7 @@ public class EnchantmentMagicProtection extends Enchantment {
public enum Type { public enum Type {
MAGIC("magic", 1, 5, 8, s -> s instanceof IElementalDamage), MAGIC("magic", 1, 5, 8, s -> s instanceof IElementalDamage || s.isMagicDamage()),
FROST("frost", 2, 10, 8, s -> s instanceof IElementalDamage && ((IElementalDamage)s).getType() == MagicDamage.DamageType.FROST), FROST("frost", 2, 10, 8, s -> s instanceof IElementalDamage && ((IElementalDamage)s).getType() == MagicDamage.DamageType.FROST),
SHOCK("shock", 2, 10, 8, s -> s instanceof IElementalDamage && ((IElementalDamage)s).getType() == MagicDamage.DamageType.SHOCK); SHOCK("shock", 2, 10, 8, s -> s instanceof IElementalDamage && ((IElementalDamage)s).getType() == MagicDamage.DamageType.SHOCK);
// Fire already exists, and the other types aren't used enough to be worth having // Fire already exists, and the other types aren't used enough to be worth having
@@ -120,9 +120,9 @@ public interface Imbuement {
ItemStack bow = archer.getHeldItemMainhand(); ItemStack bow = archer.getHeldItemMainhand();
if(!ImbueWeapon.isBow(bow.getItem())){ if(!ImbueWeapon.isBow(bow)){
bow = archer.getHeldItemOffhand(); bow = archer.getHeldItemOffhand();
if(!ImbueWeapon.isBow(bow.getItem())) return; if(!ImbueWeapon.isBow(bow)) return;
} }
// Taken directly from ItemBow, so it works exactly the same as the power enchantment. // Taken directly from ItemBow, so it works exactly the same as the power enchantment.
@@ -5,6 +5,7 @@ import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.AllyDesignationSystem; import electroblob.wizardry.util.AllyDesignationSystem;
import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.NBTExtras;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.block.Block; import net.minecraft.block.Block;
@@ -183,7 +184,7 @@ public class EntityLevitatingBlock extends EntityFallingBlock implements IEntity
NBTBase nbtbase = this.tileEntityData.getTag(s); NBTBase nbtbase = this.tileEntityData.getTag(s);
if(!"x".equals(s) && !"y".equals(s) && !"z".equals(s)){ if(!"x".equals(s) && !"y".equals(s) && !"z".equals(s)){
nbttagcompound.setTag(s, nbtbase.copy()); NBTExtras.storeTagSafely(nbttagcompound, s, nbtbase.copy());
} }
} }
@@ -5,9 +5,11 @@ import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Meteor; import electroblob.wizardry.spell.Meteor;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.MoverType; import net.minecraft.entity.MoverType;
import net.minecraft.entity.item.EntityFallingBlock; import net.minecraft.entity.item.EntityFallingBlock;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundCategory;
import net.minecraft.world.World; import net.minecraft.world.World;
@@ -61,9 +63,9 @@ public class EntityMeteor extends EntityFallingBlock {
this.motionY *= 0.9800000190734863D; this.motionY *= 0.9800000190734863D;
this.motionZ *= 0.9800000190734863D; this.motionZ *= 0.9800000190734863D;
if(!this.world.isRemote){ if(this.onGround){
if(this.onGround){ if(!this.world.isRemote){
this.motionX *= 0.699999988079071D; this.motionX *= 0.699999988079071D;
this.motionZ *= 0.699999988079071D; this.motionZ *= 0.699999988079071D;
@@ -72,8 +74,13 @@ public class EntityMeteor extends EntityFallingBlock {
Spells.meteor.getProperty(Meteor.BLAST_STRENGTH).floatValue() * blastMultiplier, Spells.meteor.getProperty(Meteor.BLAST_STRENGTH).floatValue() * blastMultiplier,
damageBlocks, damageBlocks); damageBlocks, damageBlocks);
this.setDead(); this.setDead();
}else{
WizardryUtilities.getEntitiesWithinRadius(15, posX, posY, posZ, world, EntityPlayer.class)
.forEach(p -> Wizardry.proxy.shakeScreen(p, 10));
} }
} }
} }
@Override @Override
@@ -65,7 +65,7 @@ public class EntityBubble extends EntityMagicConstruct {
if(isDarkOrb){ if(isDarkOrb){
if(WizardryUtilities.getRider(this) != null if(WizardryUtilities.getRider(this) != null
&& this.ticksExisted % Spells.entrapment.getProperty(Entrapment.DAMAGE_INTERVAL).intValue() == 0){ && WizardryUtilities.getRider(this).ticksExisted % Spells.entrapment.getProperty(Entrapment.DAMAGE_INTERVAL).intValue() == 0){
if(this.getCaster() != null){ if(this.getCaster() != null){
WizardryUtilities.getRider(this).attackEntityFrom( WizardryUtilities.getRider(this).attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC),
@@ -1,6 +1,5 @@
package electroblob.wizardry.entity.construct; package electroblob.wizardry.entity.construct;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
@@ -7,7 +7,6 @@ import electroblob.wizardry.util.MagicDamage.DamageType;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityFallingBlock; import net.minecraft.entity.item.EntityFallingBlock;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.MobEffects; import net.minecraft.init.MobEffects;
import net.minecraft.network.play.server.SPacketEntityVelocity; import net.minecraft.network.play.server.SPacketEntityVelocity;
@@ -97,18 +96,6 @@ public class EntityEarthquake extends EntityMagicConstruct {
} }
} }
} }
if(!world.isRemote){
// Constant 15 blocks for now
List<EntityPlayer> targets2 = WizardryUtilities.getEntitiesWithinRadius(15, posX, posY, posZ, world, EntityPlayer.class);
float magnitude = 10f * ((float)(this.lifetime - this.ticksExisted))/(float)this.lifetime;
// Makes the screen shake
for(EntityPlayer target : targets2){
target.rotationPitch += this.ticksExisted % 2 == 0 ? magnitude : -magnitude;
}
}
} }
} }
@@ -80,8 +80,8 @@ public class EntityHammer extends EntityMagicConstruct {
if(this.world.isRemote && this.ticksExisted % 3 == 0){ if(this.world.isRemote && this.ticksExisted % 3 == 0){
ParticleBuilder.create(Type.SPARK) ParticleBuilder.create(Type.SPARK)
.pos(this.posX - 0.5d + rand.nextDouble(), this.posY + 2 * rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble()) .pos(this.posX - 0.5d + rand.nextDouble(), this.posY + 2 * rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble())
.spawn(world); .spawn(world);
} }
this.prevPosX = this.posX; this.prevPosX = this.posX;
@@ -103,39 +103,37 @@ public class EntityHammer extends EntityMagicConstruct {
this.rotationPitch = 0; this.rotationPitch = 0;
this.spin = false; this.spin = false;
if(this.ticksExisted % Spells.lightning_hammer.getProperty(LightningHammer.ATTACK_INTERVAL).floatValue() == 0){ double seekerRange = Spells.lightning_hammer.getProperty(Spell.EFFECT_RADIUS).doubleValue();
double seekerRange = Spells.lightning_hammer.getProperty(Spell.EFFECT_RADIUS).doubleValue(); List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX,
this.posY + 1, this.posZ, world);
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX, int maxTargets = Spells.lightning_hammer.getProperty(LightningHammer.SECONDARY_MAX_TARGETS).intValue();
this.posY + 1, this.posZ, world); while(targets.size() > maxTargets) targets.remove(targets.size() - 1);
int maxTargets = Spells.lightning_hammer.getProperty(LightningHammer.SECONDARY_MAX_TARGETS).intValue(); for(EntityLivingBase target : targets){
while(targets.size() > maxTargets) targets.remove(targets.size() - 1);
for(EntityLivingBase target : targets){ if(WizardryUtilities.isLiving(target) && this.isValidTarget(target)
&& target.ticksExisted % Spells.lightning_hammer.getProperty(LightningHammer.ATTACK_INTERVAL).floatValue() == 0){
if(WizardryUtilities.isLiving(target) && this.isValidTarget(target)){ if(world.isRemote){
if(world.isRemote){ ParticleBuilder.create(Type.LIGHTNING).pos(posX, posY + height - 0.1, posZ) .target(target).spawn(world);
ParticleBuilder.create(Type.LIGHTNING).pos(posX, posY + height - 0.1, posZ) .target(target).spawn(world); ParticleBuilder.spawnShockParticles(world, target.posX,
target.getEntityBoundingBox().minY + target.height, target.posZ);
}
ParticleBuilder.spawnShockParticles(world, target.posX, target.playSound(WizardrySounds.ENTITY_HAMMER_ATTACK, 1.0F, rand.nextFloat() * 0.4F + 1.5F);
target.getEntityBoundingBox().minY + target.height, target.posZ);
}
target.playSound(WizardrySounds.ENTITY_HAMMER_ATTACK, 1.0F, rand.nextFloat() * 0.4F + 1.5F); float damage = Spells.lightning_hammer.getProperty(Spell.SPLASH_DAMAGE).floatValue() * damageMultiplier;
float damage = Spells.lightning_hammer.getProperty(Spell.SPLASH_DAMAGE).floatValue() * damageMultiplier; if(this.getCaster() != null){
WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(
if(this.getCaster() != null){ this, getCaster(), DamageType.SHOCK), damage);
WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage( WizardryUtilities.applyStandardKnockback(this, target);
this, getCaster(), DamageType.SHOCK), damage); }else{
WizardryUtilities.applyStandardKnockback(this, target); target.attackEntityFrom(DamageSource.MAGIC, damage);
}else{
target.attackEntityFrom(DamageSource.MAGIC, damage);
}
} }
} }
} }
@@ -171,6 +169,7 @@ public class EntityHammer extends EntityMagicConstruct {
public void fall(float distance, float damageMultiplier){ public void fall(float distance, float damageMultiplier){
if(world.isRemote){ if(world.isRemote){
for(int i = 0; i < 40; i++){ for(int i = 0; i < 40; i++){
double particleX = this.posX - 1.0d + 2 * rand.nextDouble(); double particleX = this.posX - 1.0d + 2 * rand.nextDouble();
double particleZ = this.posZ - 1.0d + 2 * rand.nextDouble(); double particleZ = this.posZ - 1.0d + 2 * rand.nextDouble();
@@ -182,6 +181,12 @@ public class EntityHammer extends EntityMagicConstruct {
particleX - this.posX, 0, particleZ - this.posZ, Block.getStateId(block)); particleX - this.posX, 0, particleZ - this.posZ, Block.getStateId(block));
} }
} }
if(this.fallDistance > 10){
WizardryUtilities.getEntitiesWithinRadius(10, posX, posY, posZ, world, EntityPlayer.class)
.forEach(p -> Wizardry.proxy.shakeScreen(p, 6));
}
}else{ }else{
// Just to check the hammer has actually fallen from the sky, rather than the block under it being broken. // Just to check the hammer has actually fallen from the sky, rather than the block under it being broken.
if(this.fallDistance > 10){ if(this.fallDistance > 10){
@@ -62,7 +62,7 @@ public class EntityHealAura extends EntityMagicConstruct {
target.motionZ = velZ; target.motionZ = velZ;
} }
}else if(target.getHealth() < target.getMaxHealth() && this.ticksExisted % 5 == 0){ }else if(target.getHealth() < target.getMaxHealth() && target.ticksExisted % 5 == 0){
target.heal(Spells.healing_aura.getProperty(Spell.HEALTH).floatValue() * damageMultiplier); target.heal(Spells.healing_aura.getProperty(Spell.HEALTH).floatValue() * damageMultiplier);
} }
} }
@@ -10,6 +10,7 @@ import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.MoverType; import net.minecraft.entity.MoverType;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@@ -61,10 +62,9 @@ public class EntityIceSpike extends EntityMagicConstruct {
if(!this.world.isRemote){ if(!this.world.isRemote){
for(Object entity : this.world.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox())){ for(Object entity : this.world.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox())){
if(entity instanceof EntityLivingBase && this.isValidTarget((EntityLivingBase)entity)){ if(entity instanceof EntityLivingBase && this.isValidTarget((EntityLivingBase)entity)){
DamageSource source = this.getCaster() == null ? DamageSource.MAGIC : MagicDamage.causeDirectMagicDamage(this.getCaster(), DamageType.FROST);
// Potion effect only gets added if the damage succeeded. // Potion effect only gets added if the damage succeeded.
if(((EntityLivingBase)entity).attackEntityFrom( if(((EntityLivingBase)entity).attackEntityFrom(source, Spells.ice_spikes.getProperty(Spell.DAMAGE).floatValue() * this.damageMultiplier))
MagicDamage.causeDirectMagicDamage(this.getCaster(), DamageType.FROST),
Spells.ice_spikes.getProperty(Spell.DAMAGE).floatValue() * this.damageMultiplier))
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(WizardryPotions.frost, ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(WizardryPotions.frost,
Spells.ice_spikes.getProperty(Spell.EFFECT_DURATION).intValue(), Spells.ice_spikes.getProperty(Spell.EFFECT_DURATION).intValue(),
Spells.ice_spikes.getProperty(Spell.EFFECT_STRENGTH).intValue())); Spells.ice_spikes.getProperty(Spell.EFFECT_STRENGTH).intValue()));
@@ -213,6 +213,7 @@ public class EntityAIAttackSpell<T extends EntityLiving & ISpellCaster> extends
return false; return false;
} }
// This is only called when spell casting starts so ticksInUse is always zero
if(spell.cast(attacker.world, attacker, EnumHand.MAIN_HAND, 0, target, modifiers)){ if(spell.cast(attacker.world, attacker, EnumHand.MAIN_HAND, 0, target, modifiers)){
if(spell.isContinuous){ if(spell.isContinuous){
@@ -74,6 +74,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
// Field implementations // Field implementations
private List<Spell> spells = new ArrayList<Spell>(4); private List<Spell> spells = new ArrayList<Spell>(4);
private Spell continuousSpell; private Spell continuousSpell;
private int spellCounter;
public EntityEvilWizard(World world){ public EntityEvilWizard(World world){
@@ -175,6 +176,16 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
public Spell getContinuousSpell(){ public Spell getContinuousSpell(){
return this.continuousSpell; return this.continuousSpell;
} }
@Override
public void setSpellCounter(int count){
spellCounter = count;
}
@Override
public int getSpellCounter(){
return spellCounter;
}
@Override @Override
public int getAimingError(EnumDifficulty difficulty){ public int getAimingError(EnumDifficulty difficulty){
@@ -252,7 +263,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
// When right-clicked with a spell book in creative, sets one of the spells to that spell // When right-clicked with a spell book in creative, sets one of the spells to that spell
if(player.isCreative() && stack.getItem() instanceof ItemSpellBook){ if(player.isCreative() && stack.getItem() instanceof ItemSpellBook){
Spell spell = Spell.byMetadata(stack.getItemDamage()); Spell spell = Spell.byMetadata(stack.getItemDamage());
if(this.spells.size() >= 4 && spell.canBeCastByNPCs()){ if(this.spells.size() >= 4 && spell.canBeCastBy(this, true)){
// The set(...) method returns the element that was replaced - neat! // The set(...) method returns the element that was replaced - neat!
player.sendMessage(new TextComponentTranslation("item." + Wizardry.MODID + ":spell_book.apply_to_wizard", player.sendMessage(new TextComponentTranslation("item." + Wizardry.MODID + ":spell_book.apply_to_wizard",
this.getDisplayName(), this.spells.set(rand.nextInt(3) + 1, spell).getNameForTranslationFormatted(), this.getDisplayName(), this.spells.set(rand.nextInt(3) + 1, spell).getNameForTranslationFormatted(),
@@ -269,9 +280,9 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
super.writeEntityToNBT(nbt); super.writeEntityToNBT(nbt);
nbt.setInteger("element", this.getElement().ordinal()); nbt.setInteger("element", this.getElement().ordinal());
nbt.setInteger("skin", this.textureIndex); nbt.setInteger("skin", this.textureIndex);
nbt.setTag("spells", NBTExtras.listToNBT(spells, spell -> new NBTTagInt(spell.metadata()))); NBTExtras.storeTagSafely(nbt, "spells", NBTExtras.listToNBT(spells, spell -> new NBTTagInt(spell.metadata())));
nbt.setBoolean("hasStructure", this.hasStructure); nbt.setBoolean("hasStructure", this.hasStructure);
nbt.setTag("groupUUIDs", NBTExtras.listToNBT(groupUUIDs, NBTUtil::createUUIDTag)); NBTExtras.storeTagSafely(nbt, "groupUUIDs", NBTExtras.listToNBT(groupUUIDs, NBTUtil::createUUIDTag));
} }
@Override @Override
@@ -369,7 +380,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
// All wizards know magic missile, even if it is disabled. // All wizards know magic missile, even if it is disabled.
spells.add(Spells.magic_missile); spells.add(Spells.magic_missile);
Tier maxTier = EntityWizard.populateSpells(spells, element, hasStructure, 3, rand); Tier maxTier = EntityWizard.populateSpells(this, spells, element, hasStructure, 3, rand);
// Now done after the spells so it can take the tier into account. For evil wizards this is slightly different; // Now done after the spells so it can take the tier into account. For evil wizards this is slightly different;
// it picks a random wand which is at least a high enough tier for the spells the wizard has. // it picks a random wand which is at least a high enough tier for the spells the wizard has.
@@ -19,9 +19,6 @@ import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import java.util.Arrays;
import java.util.function.IntPredicate;
@Mod.EventBusSubscriber @Mod.EventBusSubscriber
public class EntityLightningWraith extends EntityBlazeMinion { public class EntityLightningWraith extends EntityBlazeMinion {
@@ -30,6 +30,7 @@ public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaste
private EntityAIAttackSpell<EntityPhoenix> spellAttackAI = new EntityAIAttackSpell<>(this, AISpeed, 15f, 60, 140); private EntityAIAttackSpell<EntityPhoenix> spellAttackAI = new EntityAIAttackSpell<>(this, AISpeed, 15f, 60, 140);
private Spell continuousSpell; private Spell continuousSpell;
private int spellCounter;
private static final List<Spell> attack = Collections.singletonList(Spells.flame_ray); private static final List<Spell> attack = Collections.singletonList(Spells.flame_ray);
@@ -76,6 +77,16 @@ public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaste
continuousSpell = spell; continuousSpell = spell;
} }
@Override
public void setSpellCounter(int count){
spellCounter = count;
}
@Override
public int getSpellCounter(){
return spellCounter;
}
@Override @Override
public boolean hasRangedAttack(){ public boolean hasRangedAttack(){
return true; return true;
@@ -61,21 +61,6 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell
return attack; return attack;
} }
@Override
public SpellModifiers getModifiers(){
return new SpellModifiers();
}
@Override
public Spell getContinuousSpell(){
return Spells.none;
}
@Override
public void setContinuousSpell(Spell spell){
// Doesn't use continuous spells.
}
@Override @Override
protected void applyEntityAttributes(){ protected void applyEntityAttributes(){
super.applyEntityAttributes(); super.applyEntityAttributes();
@@ -59,21 +59,6 @@ public class EntityStormElemental extends EntitySummonedCreature implements ISpe
return attack; return attack;
} }
@Override
public SpellModifiers getModifiers(){
return new SpellModifiers();
}
@Override
public Spell getContinuousSpell(){
return Spells.none;
}
@Override
public void setContinuousSpell(Spell spell){
// Doesn't use continuous spells.
}
@Override @Override
protected void applyEntityAttributes(){ protected void applyEntityAttributes(){
super.applyEntityAttributes(); super.applyEntityAttributes();
@@ -50,6 +50,7 @@ import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
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 org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
@@ -83,6 +84,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
// Field implementations // Field implementations
private List<Spell> spells = new ArrayList<Spell>(4); private List<Spell> spells = new ArrayList<Spell>(4);
private Spell continuousSpell; private Spell continuousSpell;
private int spellCounter;
/** A set of the positions of the blocks that are part of this wizard's tower. */ /** A set of the positions of the blocks that are part of this wizard's tower. */
private Set<BlockPos> towerBlocks; private Set<BlockPos> towerBlocks;
@@ -187,7 +189,17 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
public Spell getContinuousSpell(){ public Spell getContinuousSpell(){
return this.continuousSpell; return this.continuousSpell;
} }
@Override
public void setSpellCounter(int count){
spellCounter = count;
}
@Override
public int getSpellCounter(){
return spellCounter;
}
@Override @Override
public int getAimingError(EnumDifficulty difficulty){ public int getAimingError(EnumDifficulty difficulty){
// Being more intelligent than skeletons, wizards are a little more accurate. // Being more intelligent than skeletons, wizards are a little more accurate.
@@ -359,7 +371,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
// When right-clicked with a spell book in creative, sets one of the spells to that spell // When right-clicked with a spell book in creative, sets one of the spells to that spell
if(player.isCreative() && stack.getItem() instanceof ItemSpellBook){ if(player.isCreative() && stack.getItem() instanceof ItemSpellBook){
Spell spell = Spell.byMetadata(stack.getItemDamage()); Spell spell = Spell.byMetadata(stack.getItemDamage());
if(this.spells.size() >= 4 && spell.canBeCastByNPCs()){ if(this.spells.size() >= 4 && spell.canBeCastBy(this, true)){
// The set(...) method returns the element that was replaced - neat! // The set(...) method returns the element that was replaced - neat!
player.sendMessage(new TextComponentTranslation("item." + Wizardry.MODID + ":spell_book.apply_to_wizard", player.sendMessage(new TextComponentTranslation("item." + Wizardry.MODID + ":spell_book.apply_to_wizard",
this.getDisplayName(), this.spells.set(rand.nextInt(3) + 1, spell).getNameForTranslationFormatted(), this.getDisplayName(), this.spells.set(rand.nextInt(3) + 1, spell).getNameForTranslationFormatted(),
@@ -389,15 +401,15 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
super.writeEntityToNBT(nbt); super.writeEntityToNBT(nbt);
if(this.trades != null){ if(this.trades != null){
nbt.setTag("trades", this.trades.getRecipiesAsTags()); NBTExtras.storeTagSafely(nbt, "trades", this.trades.getRecipiesAsTags());
} }
nbt.setInteger("element", this.getElement().ordinal()); nbt.setInteger("element", this.getElement().ordinal());
nbt.setInteger("skin", this.textureIndex); nbt.setInteger("skin", this.textureIndex);
nbt.setTag("spells", NBTExtras.listToNBT(spells, spell -> new NBTTagInt(spell.metadata()))); NBTExtras.storeTagSafely(nbt, "spells", NBTExtras.listToNBT(spells, spell -> new NBTTagInt(spell.metadata())));
if(this.towerBlocks != null && this.towerBlocks.size() > 0){ if(this.towerBlocks != null && this.towerBlocks.size() > 0){
nbt.setTag("towerBlocks", NBTExtras.listToNBT(this.towerBlocks, NBTUtil::createPosTag)); NBTExtras.storeTagSafely(nbt, "towerBlocks", NBTExtras.listToNBT(this.towerBlocks, NBTUtil::createPosTag));
} }
} }
@@ -491,7 +503,6 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
ItemStack anySpellBook = new ItemStack(WizardryItems.spell_book, 1, OreDictionary.WILDCARD_VALUE); ItemStack anySpellBook = new ItemStack(WizardryItems.spell_book, 1, OreDictionary.WILDCARD_VALUE);
ItemStack crystalStack = new ItemStack(WizardryItems.magic_crystal, 5); ItemStack crystalStack = new ItemStack(WizardryItems.magic_crystal, 5);
// NOTE: For wizardry 1.2, increase the number of uses of this trade. The default is 7, for reference.
this.trades.add(new MerchantRecipe(anySpellBook, crystalStack)); this.trades.add(new MerchantRecipe(anySpellBook, crystalStack));
this.addRandomRecipes(3); this.addRandomRecipes(3);
@@ -576,12 +587,14 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
// TODO: Switch all of this over to some kind of loot pool system? // TODO: Switch all of this over to some kind of loot pool system?
@SuppressWarnings("unchecked")
private ItemStack getRandomPrice(Tier tier){ private ItemStack getRandomPrice(Tier tier){
Map<ResourceLocation, Integer> map = Wizardry.settings.currencyItems; Map<Pair<ResourceLocation, Short>, Integer> map = Wizardry.settings.currencyItems;
// This isn't that efficient but it's not called very often really so it doesn't matter // This isn't that efficient but it's not called very often really so it doesn't matter
ResourceLocation itemName = map.keySet().toArray(new ResourceLocation[0])[rand.nextInt(map.size())]; Pair<ResourceLocation, Short> itemName = map.keySet().toArray(new Pair[0])[rand.nextInt(map.size())];
Item item = Item.REGISTRY.getObject(itemName); Item item = Item.REGISTRY.getObject(itemName.getLeft());
short meta = itemName.getRight();
int value; int value;
if(item == null){ if(item == null){
@@ -595,7 +608,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
// ((tier.ordinal() + 1) * 16 + rand.nextInt(6)) gives a 'value' for the item being bought // ((tier.ordinal() + 1) * 16 + rand.nextInt(6)) gives a 'value' for the item being bought
// This is then divided by the value of the currency item to give a price // This is then divided by the value of the currency item to give a price
// The absolute maximum stack size that can result from this calculation (with value = 1) is 64. // The absolute maximum stack size that can result from this calculation (with value = 1) is 64.
return new ItemStack(item, (8 + tier.ordinal() * 16 + rand.nextInt(9)) / value); return new ItemStack(item, (8 + tier.ordinal() * 16 + rand.nextInt(9)) / value, meta);
} }
private ItemStack getRandomItemOfTier(Tier tier){ private ItemStack getRandomItemOfTier(Tier tier){
@@ -752,7 +765,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
// All wizards know magic missile, even if it is disabled. // All wizards know magic missile, even if it is disabled.
spells.add(Spells.magic_missile); spells.add(Spells.magic_missile);
Tier maxTier = populateSpells(spells, element, false, 3, rand); Tier maxTier = populateSpells(this, spells, element, false, 3, rand);
// Now done after the spells so it can take the tier into account. // Now done after the spells so it can take the tier into account.
ItemStack wand = new ItemStack(WizardryItems.getWand(maxTier, element)); ItemStack wand = new ItemStack(WizardryItems.getWand(maxTier, element));
@@ -769,19 +782,21 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
/** /**
* Adds n random spells to the given list. The spells will be of the given element if possible. Extracted as a * Adds n random spells to the given list. The spells will be of the given element if possible. Extracted as a
* separate function since it was the same in both EntityWizard and EntityEvilWizard. * separate function since it was the same in both EntityWizard and EntityEvilWizard.
* *
* @param wizard The wizard whose spells are to be populated.
* @param spells The spell list to be populated. * @param spells The spell list to be populated.
* @param e The element that the spells should belong to, or {@link Element#MAGIC} for a random element each time. * @param e The element that the spells should belong to, or {@link Element#MAGIC} for a random element each time.
* @param master Whether to include master spells.
* @param n The number of spells to add. * @param n The number of spells to add.
* @param random A random number generator to use. * @param random A random number generator to use.
* @return The tier of the highest-tier spell that was added to the list. * @return The tier of the highest-tier spell that was added to the list.
*/ */
static Tier populateSpells(List<Spell> spells, Element e, boolean master, int n, Random random){ static Tier populateSpells(final EntityLiving wizard, List<Spell> spells, Element e, boolean master, int n, Random random){
// This is the tier of the highest tier spell added. // This is the tier of the highest tier spell added.
Tier maxTier = Tier.NOVICE; Tier maxTier = Tier.NOVICE;
List<Spell> npcSpells = Spell.getSpells(Spell.npcSpells); List<Spell> npcSpells = Spell.getSpells(s -> s.canBeCastBy(wizard, false));
npcSpells.removeIf(s -> !s.applicableForItem(WizardryItems.spell_book)); npcSpells.removeIf(s -> !s.applicableForItem(WizardryItems.spell_book));
for(int i = 0; i < n; i++){ for(int i = 0; i < n; i++){
@@ -19,7 +19,9 @@ import java.util.List;
* packets) is handled by that class, and all the implementor needs to do is decide which spell(s) to select. * packets) is handled by that class, and all the implementor needs to do is decide which spell(s) to select.
* <p></p> * <p></p>
* This class also allows Wizardry to do all the syncing necessary for continuous spell casting. All the implementor * This class also allows Wizardry to do all the syncing necessary for continuous spell casting. All the implementor
* needs to do is store the actual fields involved. * needs to do is store the actual fields involved, by implementing {@link ISpellCaster#setContinuousSpell(Spell)},
* {@link ISpellCaster#getContinuousSpell()}, {@link ISpellCaster#setSpellCounter(int)}, {@link ISpellCaster#getSpellCounter()}
* and {@link ISpellCaster#getModifiers()}.
*/ */
/* Perhaps this should be a capability? Though I can't help thinking they're mainly for attaching data to vanilla /* Perhaps this should be a capability? Though I can't help thinking they're mainly for attaching data to vanilla
* classes, rather than custom ones. For now, the main purpose of this is to centralise code within wizardry itself, and * classes, rather than custom ones. For now, the main purpose of this is to centralise code within wizardry itself, and
@@ -36,10 +38,10 @@ public interface ISpellCaster {
* *
* @return A list of {@link Spell} instances. A random spell from this list will be cast when the entity attacks. * @return A list of {@link Spell} instances. A random spell from this list will be cast when the entity attacks.
* The list will not be modified by the AI class and can therefore be an immutable list. The spells in the * The list will not be modified by the AI class and can therefore be an immutable list. The spells in the
* list <b>must</b> be castable by NPCs (i.e. {@link Spell#canBeCastByNPCs()} returns true). * list <b>must</b> be castable by NPCs (i.e. {@link Spell#canBeCastBy(net.minecraft.entity.EntityLiving, boolean)} returns true).
*/ */
@Nonnull @Nonnull
public List<Spell> getSpells(); List<Spell> getSpells();
/** /**
* Called each time the entity attacks to get the modifiers to apply to the spell. * Called each time the entity attacks to get the modifiers to apply to the spell.
@@ -48,31 +50,47 @@ public interface ISpellCaster {
* required, pass in an empty {@code SpellModifiers} object. * required, pass in an empty {@code SpellModifiers} object.
*/ */
@Nonnull @Nonnull
public SpellModifiers getModifiers(); default SpellModifiers getModifiers(){
return new SpellModifiers(); // May seem wasteful but this should never be called so it doesn't matter
}
/** /**
* Returns the continuous spell that is currently being cast, or the None spell if there is none. Implementors * Returns the continuous spell that is currently being cast, or the None spell if there is none. Implementors
* should simply store this as a private field and return it here. Will be synced by the AI class, but whether it is * should simply store this as a private field and return it here. Will be synced by the AI class, but whether it is
* saved to NBT is up to you. If the implementing class does not deal with continuous spells, just return * saved to NBT is up to you. If the implementing class only ever uses one continuous spell, do <b>not</b> just
* {@link Spells#none}. If the implementing class only ever uses one continuous spell, do <b>not</b> just return * return that spell; the field must still be stored.
* that spell; the field must still be stored.
*/ */
@Nonnull @Nonnull
public Spell getContinuousSpell(); default Spell getContinuousSpell(){
return Spells.none;
}
/** /**
* Sets the continuous spell that is currently being cast, or the None spell if there is none. Implementors should * Sets the continuous spell that is currently being cast, or the None spell if there is none. Implementors should
* simply store this as a private field and assign it here. Will be synced by the AI class, but whether it is saved * simply store this as a private field and assign it here. Will be synced by the AI class, but whether it is saved
* to NBT is up to you. If the implementing class does not deal with continuous spells, leave this method blank. * to NBT is up to you. If the implementing class does not deal with continuous spells, leave this method blank.
*/ */
public void setContinuousSpell(Spell spell); default void setContinuousSpell(Spell spell){
// Do nothing
}
/** Returns the number of ticks the current spell has been cast for. Implementors should simply store this as a
* private field and return it here. This is only used client-side. */
default int getSpellCounter(){
return 0;
}
/** Sets the number of ticks the current spell has been cast for. Implementors should simply store this as a
* private field and assign it here. This is only used client-side. */
default void setSpellCounter(int count){
// Do nothing
}
/** /**
* Returns the aiming arror for the given difficulty, used in projectile spells. Defaults to the values used by * Returns the aiming error for the given difficulty, used in projectile spells. Defaults to the values used by
* skeletons, which are: Easy - 10, Normal - 6, Hard - 2, Peaceful - 10 (rarely used). * skeletons, which are: Easy - 10, Normal - 6, Hard - 2, Peaceful - 10 (rarely used).
*/ */
// This is what default methods are actually intended for! default int getAimingError(EnumDifficulty difficulty) {
public default int getAimingError(EnumDifficulty difficulty) {
return WizardryUtilities.getDefaultAimingError(difficulty); return WizardryUtilities.getDefaultAimingError(difficulty);
} }
} }
@@ -9,7 +9,6 @@ import electroblob.wizardry.util.MagicDamage.DamageType;
import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
@@ -48,13 +47,7 @@ public class EntityIceball extends EntityMagicProjectile {
}else{ }else{
boolean flag = true; if(this.getThrower() == null || WizardryUtilities.canDamageBlocks(this.getThrower(), world)){
if(this.getThrower() != null && this.getThrower() instanceof EntityLiving){
flag = net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this.getThrower());
}
if(flag){
BlockPos pos = rayTrace.getBlockPos(); BlockPos pos = rayTrace.getBlockPos();
@@ -3,6 +3,7 @@ package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.WizardryUtilities;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.entity.projectile.EntityLargeFireball; import net.minecraft.entity.projectile.EntityLargeFireball;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
@@ -50,7 +51,7 @@ public class EntityLargeMagicFireball extends EntityMagicFireball {
protected void onImpact(RayTraceResult rayTrace){ protected void onImpact(RayTraceResult rayTrace){
if(!world.isRemote){ if(!world.isRemote){
boolean flag = net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this.thrower); boolean flag = this.getThrower() == null || WizardryUtilities.canDamageBlocks(this.getThrower(), world);
this.world.newExplosion(null, this.posX, this.posY, this.posZ, getExplosionPower() * blastMultiplier, flag, flag); this.world.newExplosion(null, this.posX, this.posY, this.posZ, getExplosionPower() * blastMultiplier, flag, flag);
} }
@@ -6,14 +6,18 @@ 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.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.WizardryUtilities;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.projectile.EntitySmallFireball; import net.minecraft.entity.projectile.EntitySmallFireball;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.DamageSource;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@@ -80,13 +84,7 @@ public class EntityMagicFireball extends EntityMagicProjectile {
}else{ }else{
boolean flag = true; if(this.getThrower() == null || WizardryUtilities.canDamageBlocks(this.getThrower(), world)){
if(this.getThrower() != null && this.getThrower() instanceof EntityLiving){
flag = net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this.getThrower());
}
if(flag){
BlockPos blockpos = rayTrace.getBlockPos().offset(rayTrace.sideHit); BlockPos blockpos = rayTrace.getBlockPos().offset(rayTrace.sideHit);
@@ -131,6 +129,54 @@ public class EntityMagicFireball extends EntityMagicProjectile {
} }
} }
@Override
public boolean canBeCollidedWith(){
return true;
}
@Override
public float getCollisionBorderSize(){
return 1.0F;
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount){
if(this.isEntityInvulnerable(source)){
return false;
}else{
this.markVelocityChanged();
if(source.getTrueSource() != null){
Vec3d vec3d = source.getTrueSource().getLookVec();
if(vec3d != null){
double speed = MathHelper.sqrt(motionX * motionX + motionY * motionY + motionZ * motionZ);
this.motionX = vec3d.x * speed;
this.motionY = vec3d.y * speed;
this.motionZ = vec3d.z * speed;
this.lifetime = 160;
}
if(source.getTrueSource() instanceof EntityLivingBase){
this.setCaster((EntityLivingBase)source.getTrueSource());
}
return true;
}else{
return false;
}
}
}
public void setLifetime(int lifetime){ public void setLifetime(int lifetime){
this.lifetime = lifetime; this.lifetime = lifetime;
} }
@@ -71,7 +71,7 @@ public abstract class EntityMagicProjectile extends EntityThrowable implements I
double dy = !this.hasNoGravity() ? target.getEntityBoundingBox().minY + (double)(target.height / 3.0f) - this.posY double dy = !this.hasNoGravity() ? target.getEntityBoundingBox().minY + (double)(target.height / 3.0f) - this.posY
: target.getEntityBoundingBox().minY + (double)(target.height / 2.0f) - this.posY; : target.getEntityBoundingBox().minY + (double)(target.height / 2.0f) - this.posY;
double dz = target.posZ - caster.posZ; double dz = target.posZ - caster.posZ;
double horizontalDistance = (double)MathHelper.sqrt(dx * dx + dz * dz); double horizontalDistance = MathHelper.sqrt(dx * dx + dz * dz);
if(horizontalDistance >= 1.0E-7D){ if(horizontalDistance >= 1.0E-7D){
@@ -9,7 +9,6 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.MobEffects; import net.minecraft.init.MobEffects;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.EnumParticleTypes;
@@ -1,11 +1,17 @@
package electroblob.wizardry.integration.antiqueatlas; package electroblob.wizardry.integration.antiqueatlas;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import hunternif.mc.atlas.AntiqueAtlasMod;
import hunternif.mc.atlas.api.AtlasAPI; import hunternif.mc.atlas.api.AtlasAPI;
import hunternif.mc.atlas.marker.GlobalMarkersData;
import hunternif.mc.atlas.marker.Marker;
import hunternif.mc.atlas.registry.MarkerType; import hunternif.mc.atlas.registry.MarkerType;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
/** /**
* This class handles all of wizardry's integration with the <i>Antique Atlas</i> mod. This class contains only the code * This class handles all of wizardry's integration with the <i>Antique Atlas</i> mod. This class contains only the code
@@ -15,6 +21,7 @@ import net.minecraftforge.fml.common.Loader;
* @since Wizardry 4.2 * @since Wizardry 4.2
* @author Electroblob * @author Electroblob
*/ */
@Mod.EventBusSubscriber
public class WizardryAntiqueAtlasIntegration { public class WizardryAntiqueAtlasIntegration {
public static final String ANTIQUE_ATLAS_MOD_ID = "antiqueatlas"; public static final String ANTIQUE_ATLAS_MOD_ID = "antiqueatlas";
@@ -38,7 +45,7 @@ public class WizardryAntiqueAtlasIntegration {
* {@link electroblob.wizardry.Settings#autoTowerMarkers} is enabled. Server side only! */ * {@link electroblob.wizardry.Settings#autoTowerMarkers} is enabled. Server side only! */
public static void markTower(World world, int x, int z){ public static void markTower(World world, int x, int z){
if(enabled() && Wizardry.settings.autoTowerMarkers){ if(enabled() && Wizardry.settings.autoTowerMarkers){
AtlasAPI.getMarkerAPI().putGlobalMarker(world, false, TOWER_MARKER.toString(), "integration.antiqueatlas.marker." + TOWER_MARKER.toString(), x, z); AtlasAPI.getMarkerAPI().putGlobalMarker(world, false, TOWER_MARKER.toString(), "integration.antiqueatlas.marker." + TOWER_MARKER.toString().replace(':', '.'), x, z);
} }
} }
@@ -46,7 +53,7 @@ public class WizardryAntiqueAtlasIntegration {
* {@link electroblob.wizardry.Settings#autoObeliskMarkers} is enabled. Server side only! */ * {@link electroblob.wizardry.Settings#autoObeliskMarkers} is enabled. Server side only! */
public static void markObelisk(World world, int x, int z){ public static void markObelisk(World world, int x, int z){
if(enabled() && Wizardry.settings.autoObeliskMarkers){ if(enabled() && Wizardry.settings.autoObeliskMarkers){
AtlasAPI.getMarkerAPI().putGlobalMarker(world, false, OBELISK_MARKER.toString(), "integration.antiqueatlas.marker." + OBELISK_MARKER.toString(), x, z); AtlasAPI.getMarkerAPI().putGlobalMarker(world, false, OBELISK_MARKER.toString(), "integration.antiqueatlas.marker." + OBELISK_MARKER.toString().replace(':', '.'), x, z);
} }
} }
@@ -54,7 +61,7 @@ public class WizardryAntiqueAtlasIntegration {
* {@link electroblob.wizardry.Settings#autoShrineMarkers} is enabled. Server side only! */ * {@link electroblob.wizardry.Settings#autoShrineMarkers} is enabled. Server side only! */
public static void markShrine(World world, int x, int z){ public static void markShrine(World world, int x, int z){
if(enabled() && Wizardry.settings.autoShrineMarkers){ if(enabled() && Wizardry.settings.autoShrineMarkers){
AtlasAPI.getMarkerAPI().putGlobalMarker(world, false, SHRINE_MARKER.toString(), "integration.antiqueatlas.marker." + SHRINE_MARKER.toString(), x, z); AtlasAPI.getMarkerAPI().putGlobalMarker(world, false, SHRINE_MARKER.toString(), "integration.antiqueatlas.marker." + SHRINE_MARKER.toString().replace(':', '.'), x, z);
} }
} }
@@ -68,4 +75,21 @@ public class WizardryAntiqueAtlasIntegration {
AtlasAPI.getMarkerAPI().registerMarker(new MarkerType(OBELISK_MARKER, new ResourceLocation(Wizardry.MODID, "textures/integration/antiqueatlas/obelisk.png"))); AtlasAPI.getMarkerAPI().registerMarker(new MarkerType(OBELISK_MARKER, new ResourceLocation(Wizardry.MODID, "textures/integration/antiqueatlas/obelisk.png")));
} }
@SubscribeEvent
public static void onWorldLoadEvent(WorldEvent.Load event){
if(!enabled()) return;
// Backwards compatibility for existing markers using the old translation key format (with colons)
GlobalMarkersData data = AntiqueAtlasMod.globalMarkersData.getData();
for(Marker marker : data.getMarkersInDimension(event.getWorld().provider.getDimension())){
if(marker.getLabel().contains(":")){
// Remove old-format markers and replace them with new ones
data.removeMarker(marker.getId());
AtlasAPI.getMarkerAPI().putGlobalMarker(event.getWorld(), marker.isVisibleAhead(), marker.getType(),
marker.getLabel().replace(':', '.'), marker.getX(), marker.getZ());
}
}
}
} }
@@ -61,11 +61,10 @@ public class ItemArcaneTome extends Item {
Tier tier = Tier.values()[stack.getItemDamage()]; Tier tier = Tier.values()[stack.getItemDamage()];
Tier tier2 = Tier.values()[stack.getItemDamage() - 1]; Tier tier2 = Tier.values()[stack.getItemDamage() - 1];
tooltip.add(tier.getDisplayNameWithFormatting()); tooltip.add(tier.getDisplayNameWithFormatting());
tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":arcane_tome.desc1", Wizardry.proxy.addMultiLineDescription(tooltip, "item." + this.getRegistryName() + ".desc",
tier2.getDisplayNameWithFormatting())); tier2.getDisplayNameWithFormatting() + "\u00A77", tier.getDisplayNameWithFormatting() + "\u00A77");
tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":arcane_tome.desc2",
tier.getDisplayNameWithFormatting() + "\u00A77"));
} }
} }
@@ -375,7 +375,7 @@ public class ItemArtefact extends Item {
if(artefact == WizardryItems.ring_battlemage){ if(artefact == WizardryItems.ring_battlemage){
if(player.getHeldItemOffhand().getItem() instanceof ISpellCastingItem if(player.getHeldItemOffhand().getItem() instanceof ISpellCastingItem
&& ImbueWeapon.isSword(player.getHeldItemMainhand().getItem())){ && ImbueWeapon.isSword(player.getHeldItemMainhand())){
modifiers.set(SpellModifiers.POTENCY, 1.1f * potency, false); modifiers.set(SpellModifiers.POTENCY, 1.1f * potency, false);
} }
@@ -1,7 +1,6 @@
package electroblob.wizardry.item; package electroblob.wizardry.item;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.data.SpellGlyphData;
import electroblob.wizardry.event.SpellCastEvent; import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.event.SpellCastEvent.Source; import electroblob.wizardry.event.SpellCastEvent.Source;
import electroblob.wizardry.packet.PacketCastSpell; import electroblob.wizardry.packet.PacketCastSpell;
@@ -20,7 +19,6 @@ import net.minecraftforge.common.MinecraftForge;
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 net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.oredict.OreDictionary;
import java.util.List; import java.util.List;
@@ -52,7 +50,7 @@ public class ItemScroll extends Item implements ISpellCastingItem {
if(tab == WizardryTabs.SPELLS){ if(tab == WizardryTabs.SPELLS){
List<Spell> spells = Spell.getSpells(Spell.allSpells); List<Spell> spells = Spell.getAllSpells();
spells.removeIf(s -> !s.applicableForItem(this)); spells.removeIf(s -> !s.applicableForItem(this));
for(Spell spell : spells){ for(Spell spell : spells){
@@ -41,7 +41,7 @@ public class ItemSpellBook extends Item {
if(tab == WizardryTabs.SPELLS){ if(tab == WizardryTabs.SPELLS){
List<Spell> spells = Spell.getSpells(Spell.allSpells); List<Spell> spells = Spell.getAllSpells();
spells.removeIf(s -> !s.applicableForItem(this)); spells.removeIf(s -> !s.applicableForItem(this));
for(Spell spell : spells){ for(Spell spell : spells){
@@ -27,7 +27,10 @@ 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.*; import net.minecraft.util.ActionResult;
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;
@@ -330,7 +330,7 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
IAttributeInstance attribute = event.getEntityLiving().getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE); IAttributeInstance attribute = event.getEntityLiving().getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE);
double followRange = attribute == null ? 16 : attribute.getAttributeValue(); double followRange = attribute == null ? 16 : attribute.getAttributeValue();
if(event.getTarget().isSneaking()) followRange *= 0.8; if(event.getTarget().isSneaking()) followRange *= 0.8;
float f = armourPieces / ((EntityPlayer)event.getTarget()).inventory.armorInventory.size(); float f = (float)armourPieces / ((EntityPlayer)event.getTarget()).inventory.armorInventory.size();
if(f < 0.1F) f = 0.1F; if(f < 0.1F) f = 0.1F;
followRange *= (double)(0.7F * f); followRange *= (double)(0.7F * f);
// Don't need to worry about the isSuitableTarget check since it must already have been checked to get this far // Don't need to worry about the isSuitableTarget check since it must already have been checked to get this far
@@ -7,6 +7,7 @@ import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.data.WizardData; import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.ItemScroll; import electroblob.wizardry.item.ItemScroll;
import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.SpellProperties; import electroblob.wizardry.util.SpellProperties;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
@@ -16,9 +17,9 @@ import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.loot.LootContext; import net.minecraft.world.storage.loot.LootContext;
import net.minecraft.world.storage.loot.conditions.LootCondition; import net.minecraft.world.storage.loot.conditions.LootCondition;
import net.minecraft.world.storage.loot.functions.LootFunction; import net.minecraft.world.storage.loot.functions.LootFunction;
import org.apache.commons.lang3.ArrayUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@@ -76,89 +77,94 @@ public class RandomSpell extends LootFunction {
if(!(stack.getItem() instanceof ItemSpellBook) && !(stack.getItem() instanceof ItemScroll)) Wizardry.logger if(!(stack.getItem() instanceof ItemSpellBook) && !(stack.getItem() instanceof ItemScroll)) Wizardry.logger
.warn("Applying the random_spell loot function to an item that isn't a spell book or scroll."); .warn("Applying the random_spell loot function to an item that isn't a spell book or scroll.");
Tier tier;
Element element;
if(ignoreWeighting){
if(tiers == null || tiers.isEmpty()){
tier = Tier.values()[random.nextInt(Tier.values().length)];
}else{
tier = tiers.get(random.nextInt(tiers.size()));
}
}else{
if(tiers == null || tiers.isEmpty()){
tier = Tier.getWeightedRandomTier(random);
}else{
tier = Tier.getWeightedRandomTier(random, tiers.toArray(new Tier[0]));
}
}
// Elements aren't weighted
if(elements == null || elements.isEmpty()){
// Element can only be MAGIC if tier is NOVICE
if(tier == Tier.NOVICE){
element = Element.values()[random.nextInt(Element.values().length)];
}else{
Element[] elements = ArrayUtils.removeElement(Element.values(), Element.MAGIC);
element = elements[random.nextInt(elements.length)];
}
}else{
// In theory, swapping this line to the commented one should make absolutely no difference.
element = elements.get(random.nextInt(elements.size()));
// element = null;
}
SpellProperties.Context spellContext = context.getLootedEntity() == null ? SpellProperties.Context.TREASURE SpellProperties.Context spellContext = context.getLootedEntity() == null ? SpellProperties.Context.TREASURE
: SpellProperties.Context.LOOTING; : SpellProperties.Context.LOOTING;
// 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?
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 ItemSpellBook) spellsList.removeIf(s -> !s.isEnabled(SpellProperties.Context.BOOK));
// Ensures the tier chosen actually has spells in it, and if not uses NOVICE instead. NOVICE always has at least
// the NONE spell since this spell cannot be disabled.
// Commented out for now because it will interfere with the ability to specify tiers and elements.
// To be honest, I may as well just say that if you disable enough spells to make this important, you deserve
// less loot!
/* if(spells.isEmpty()){ spellsList = Spell.getSpells(new Spell.TierElementFilter(EnumTier.NOVICE, null));
* if(stack.getItem() instanceof ItemScroll) spellsList.retainAll(Spell.getSpells(Spell.nonContinuousSpells));
* } */
if(spells != null && !spells.isEmpty()){
spellsList.retainAll(spells);
}
// This method is badly-named, loot chests pass a player through too, not just mobs // This method is badly-named, loot chests pass a player through too, not just mobs
// (And WHY does it only return an entity?! The underlying field is always a player so I'm casting it anyway) // (And WHY does it only return an entity?! The underlying field is always a player so I'm casting it anyway)
EntityPlayer player = (EntityPlayer)context.getKillerPlayer(); EntityPlayer player = (EntityPlayer)context.getKillerPlayer();
Spell spell = pickRandomSpell(stack, random, spellContext, player);
if(spell == Spells.none) Wizardry.logger.warn("Tried to apply the random_spell loot function to an item, but no"
+ " enabled spells matched the criteria specified. Substituting placeholder (metadata 0) item.");
stack.setItemDamage(spell.metadata());
return stack;
}
private Spell pickRandomSpell(ItemStack stack, Random random, SpellProperties.Context spellContext, EntityPlayer player){
// We're now doing this first because we need to know which spells we have to play with before selecting a tier and element
List<Spell> possibleSpells = Spell.getSpells(s -> s.isEnabled(spellContext) && s.applicableForItem(stack.getItem())
// Remove excluded tiers/elements immediately (mainly because empty tier checks should account for excluded elements)
&& (tiers == null || tiers.contains(s.getTier())) && (elements == null || elements.contains(s.getElement())));
if(spells != null && !spells.isEmpty()){
possibleSpells.retainAll(spells); // Normally you wouldn't specify a spells list AND tiers/elements... but you could!
}
if(stack.getItem() instanceof ItemScroll) possibleSpells.removeIf(s -> !s.isEnabled(SpellProperties.Context.SCROLL));
if(stack.getItem() instanceof ItemSpellBook) possibleSpells.removeIf(s -> !s.isEnabled(SpellProperties.Context.BOOK));
// Select a tier...
List<Tier> possibleTiers = new ArrayList<>();
if(tiers == null || tiers.isEmpty()){
possibleTiers.addAll(Arrays.asList(Tier.values()));
}else{
possibleTiers.addAll(tiers);
}
// Remove all empty tiers
possibleTiers.removeIf(t -> possibleSpells.stream().noneMatch(s -> s.getTier() == t)); // Lambdaception!
if(possibleTiers.isEmpty()) return Spells.none; // Gotta disable a lot of spells for this to happen
Tier tier = ignoreWeighting ? possibleTiers.get(random.nextInt(possibleTiers.size()))
: Tier.getWeightedRandomTier(random, possibleTiers.toArray(new Tier[0]));
// Remove all spells that aren't of the selected tier
possibleSpells.removeIf(s -> s.getTier() != tier);
if(possibleSpells.isEmpty()) return Spells.none; // Should be caught by the no-elements check but we might as well
// Select an element...
List<Element> possibleElements = new ArrayList<>();
// Elements aren't weighted
if(elements == null || elements.isEmpty()){
possibleElements.addAll(Arrays.asList(Element.values()));
}else{
possibleElements.addAll(elements);
}
// Remove all empty elements
possibleElements.removeIf(e -> possibleSpells.stream().noneMatch(s -> s.getElement() == e));
if(possibleElements.isEmpty()) return Spells.none; // A bit more likely I guess, but still pretty unlikely
Element element = possibleElements.get(random.nextInt(possibleElements.size()));
// Remove all spells that aren't of the selected tier
possibleSpells.removeIf(s -> s.getElement() != element);
if(possibleSpells.isEmpty()) return Spells.none; // If it fails anywhere, it'll most likely be here
// Remove either the undiscovered spells or the discovered ones, depending on the bias // Remove either the undiscovered spells or the discovered ones, depending on the bias
if(undiscoveredBias > 0 && player != null){ if(undiscoveredBias > 0 && player != null){
WizardData data = WizardData.get(player); WizardData data = WizardData.get(player);
int discoveredCount = (int)spellsList.stream().filter(data::hasSpellBeenDiscovered).count(); int discoveredCount = (int)possibleSpells.stream().filter(data::hasSpellBeenDiscovered).count();
// If none have been discovered or they've all been discovered, don't bother! // If none have been discovered or they've all been discovered, don't bother!
if(discoveredCount > 0 && discoveredCount < spellsList.size()){ if(discoveredCount > 0 && discoveredCount < possibleSpells.size()){
// Kinda unintuitive but it's very neat! // Kinda unintuitive but it's very neat!
boolean keepDiscovered = random.nextFloat() < 0.5f + 0.5f * undiscoveredBias; boolean keepDiscovered = random.nextFloat() < 0.5f + 0.5f * undiscoveredBias;
spellsList.removeIf(s -> keepDiscovered != data.hasSpellBeenDiscovered(s)); possibleSpells.removeIf(s -> keepDiscovered != data.hasSpellBeenDiscovered(s));
} }
} }
if(spellsList.isEmpty()){ return possibleSpells.get(random.nextInt(possibleSpells.size())); // Finally pick a spell
Wizardry.logger.warn("Tried to apply the random_spell loot function to an item, but no enabled spells"
+ "matched the criteria specified. Substituting placeholder (metadata 0) item.");
stack.setItemDamage(0);
}else{
stack.setItemDamage(spellsList.get(random.nextInt(spellsList.size())).metadata());
}
return stack;
} }
public static class Serializer extends LootFunction.Serializer<RandomSpell> { public static class Serializer extends LootFunction.Serializer<RandomSpell> {
@@ -62,7 +62,7 @@ public class BehaviourSpellDispense extends BehaviorDispenseOptional {
if(world.isSideSolid(source.getBlockPos().offset(direction), direction.getOpposite())) return stack; if(world.isSideSolid(source.getBlockPos().offset(direction), direction.getOpposite())) return stack;
// If the scroll can never be cast by a dispenser, it should be dispensed as an item. // If the scroll can never be cast by a dispenser, it should be dispensed as an item.
if(!spell.canBeCastByDispensers()) return super.dispenseStack(source, stack); if(!spell.canBeCastBy(source.getBlockTileEntity())) return super.dispenseStack(source, stack);
SpellModifiers modifiers = new SpellModifiers(); SpellModifiers modifiers = new SpellModifiers();
@@ -1,8 +1,10 @@
package electroblob.wizardry.misc; package electroblob.wizardry.misc;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.network.PacketBuffer;
import net.minecraft.village.MerchantRecipe; import net.minecraft.village.MerchantRecipe;
import net.minecraft.village.MerchantRecipeList; import net.minecraft.village.MerchantRecipeList;
import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.oredict.OreDictionary;
@@ -46,12 +48,42 @@ public class WildcardTradeList extends MerchantRecipeList {
} }
private boolean areItemStacksExactlyEqual(ItemStack stack1, ItemStack stack2){ private boolean areItemStacksExactlyEqual(ItemStack stack1, ItemStack stack2){
// Added to allow wildcards; this line is the only actual change. // Added to allow wildcards
if((stack1.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack2.getItemDamage() == OreDictionary.WILDCARD_VALUE) if((stack1.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack2.getItemDamage() == OreDictionary.WILDCARD_VALUE)
// Can't use ItemStack.areItemsEqualIgnoreDurability because that only works for items with durability, not subtypes. // Can't use ItemStack.areItemsEqualIgnoreDurability because that only works for items with durability, not subtypes.
&& stack1.getItem() == stack2.getItem()) return true; && stack1.getItem() == stack2.getItem()) return true;
return ItemStack.areItemsEqual(stack1, stack2) && (!stack2.hasTagCompound() || stack1.hasTagCompound() && NBTUtil.areNBTEquals(stack2.getTagCompound(), stack1.getTagCompound(), false)); return ItemStack.areItemsEqual(stack1, stack2) && (!stack2.hasTagCompound() || stack1.hasTagCompound() && NBTUtil.areNBTEquals(stack2.getTagCompound(), stack1.getTagCompound(), false));
} }
@Override
public void writeToBuf(PacketBuffer buffer){
buffer.writeByte((byte)(this.size() & 255));
// Trick the client into thinking this is a normal item
for(MerchantRecipe merchantrecipe : this){
ItemStack itemToBuy = merchantrecipe.getItemToBuy();
if(itemToBuy.getMetadata() == OreDictionary.WILDCARD_VALUE) itemToBuy = WizardryUtilities.copyWithMeta(itemToBuy, 0);
buffer.writeItemStack(itemToBuy);
ItemStack itemToSell = merchantrecipe.getItemToSell();
if(itemToSell.getMetadata() == OreDictionary.WILDCARD_VALUE) itemToSell = WizardryUtilities.copyWithMeta(itemToSell, 0);
buffer.writeItemStack(itemToSell);
ItemStack secondItemToBuy = merchantrecipe.getSecondItemToBuy();
buffer.writeBoolean(!secondItemToBuy.isEmpty());
if(!secondItemToBuy.isEmpty()){
if(secondItemToBuy.getMetadata() == OreDictionary.WILDCARD_VALUE) secondItemToBuy = WizardryUtilities.copyWithMeta(secondItemToBuy, 0);
buffer.writeItemStack(secondItemToBuy);
}
buffer.writeBoolean(merchantrecipe.isRecipeDisabled());
buffer.writeInt(merchantrecipe.getToolUses());
buffer.writeInt(merchantrecipe.getMaxTradeUses());
}
}
} }
@@ -1,47 +0,0 @@
package electroblob.wizardry.packet;
import electroblob.wizardry.Wizardry;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
/** <b>[Server -> Client]</b> This packet is sent when the slow time potion effect expires or is removed from an
* entity to unblock all nearby entities' updates. */
public class PacketEndSlowTime implements IMessageHandler<PacketEndSlowTime.Message, IMessage> {
@Override
public IMessage onMessage(Message message, MessageContext ctx){
// Just to make sure that the side is correct
if(ctx.side.isClient()){
net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(() -> Wizardry.proxy.handleEndSlowTimePacket(message));
}
return null;
}
public static class Message implements IMessage {
public int hostID;
// This constructor is required otherwise you'll get errors (used somewhere in fml through reflection)
public Message(){
}
public Message(Entity host){
this.hostID = host.getEntityId();
}
@Override
public void fromBytes(ByteBuf buf){
this.hostID = buf.readInt();
}
@Override
public void toBytes(ByteBuf buf){
buf.writeInt(hostID);
}
}
}
@@ -56,6 +56,7 @@ public class PacketSyncSettings implements IMessageHandler<Message, IMessage> {
settings.discoveryMode = buf.readBoolean(); settings.discoveryMode = buf.readBoolean();
settings.creativeBypassesArcaneLock = buf.readBoolean(); settings.creativeBypassesArcaneLock = buf.readBoolean();
settings.slowTimeAffectsPlayers = buf.readBoolean(); settings.slowTimeAffectsPlayers = buf.readBoolean();
settings.replaceVanillaFireballs = buf.readBoolean();
settings.forfeitChance = buf.readFloat(); settings.forfeitChance = buf.readFloat();
} }
@@ -64,6 +65,7 @@ public class PacketSyncSettings implements IMessageHandler<Message, IMessage> {
buf.writeBoolean(settings.discoveryMode); buf.writeBoolean(settings.discoveryMode);
buf.writeBoolean(settings.creativeBypassesArcaneLock); buf.writeBoolean(settings.creativeBypassesArcaneLock);
buf.writeBoolean(settings.slowTimeAffectsPlayers); buf.writeBoolean(settings.slowTimeAffectsPlayers);
buf.writeBoolean(settings.replaceVanillaFireballs);
buf.writeFloat((float)settings.forfeitChance); // Configs don't have floats but this can only be 0-1 anyway buf.writeFloat((float)settings.forfeitChance); // Configs don't have floats but this can only be 0-1 anyway
} }
} }
@@ -26,7 +26,6 @@ public class WizardryPacketHandler {
registerMessage(PacketSpellProperties.class, PacketSpellProperties.Message.class); registerMessage(PacketSpellProperties.class, PacketSpellProperties.Message.class);
registerMessage(PacketSyncAdvancements.class, PacketSyncAdvancements.Message.class); registerMessage(PacketSyncAdvancements.class, PacketSyncAdvancements.Message.class);
registerMessage(PacketRequestAdvancementSync.class, PacketRequestAdvancementSync.Message.class); registerMessage(PacketRequestAdvancementSync.class, PacketRequestAdvancementSync.Message.class);
registerMessage(PacketEndSlowTime.class, PacketEndSlowTime.Message.class);
registerMessage(PacketResurrection.class, PacketResurrection.Message.class); registerMessage(PacketResurrection.class, PacketResurrection.Message.class);
registerMessage(PacketCastSpellAtPos.class, PacketCastSpellAtPos.Message.class); registerMessage(PacketCastSpellAtPos.class, PacketCastSpellAtPos.Message.class);
registerMessage(PacketEmitterData.class, PacketEmitterData.Message.class); registerMessage(PacketEmitterData.class, PacketEmitterData.Message.class);
@@ -2,6 +2,7 @@ package electroblob.wizardry.potion;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.util.NBTExtras;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
@@ -40,7 +41,7 @@ public class PotionContainment extends PotionMagicEffect {
// Initialise the containment position to the entity's position if it wasn't set already // Initialise the containment position to the entity's position if it wasn't set already
if(!target.getEntityData().hasKey(ENTITY_TAG)){ if(!target.getEntityData().hasKey(ENTITY_TAG)){
target.getEntityData().setTag(ENTITY_TAG, NBTUtil.createPosTag(new BlockPos(target.getPositionVector().subtract(0.5, 0.5, 0.5)))); NBTExtras.storeTagSafely(target.getEntityData(), ENTITY_TAG, NBTUtil.createPosTag(new BlockPos(target.getPositionVector().subtract(0.5, 0.5, 0.5))));
} }
Vec3d origin = WizardryUtilities.getCentre(NBTUtil.getPosFromTag(target.getEntityData().getCompoundTag(ENTITY_TAG))); Vec3d origin = WizardryUtilities.getCentre(NBTUtil.getPosFromTag(target.getEntityData().getCompoundTag(ENTITY_TAG)));
@@ -2,8 +2,6 @@ package electroblob.wizardry.potion;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.item.ItemArtefact; import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.packet.PacketEndSlowTime;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.registry.WizardryPotions;
@@ -17,11 +15,15 @@ import net.minecraft.entity.IProjectile;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
import net.minecraftforge.event.entity.living.PotionEvent; import net.minecraftforge.event.entity.living.PotionEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Mod.EventBusSubscriber @Mod.EventBusSubscriber
@@ -29,6 +31,8 @@ public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion {
// FIXME: Minecarts with entities in them (and, I suspect, any other ridden entities) go crazy when time-slowed // FIXME: Minecarts with entities in them (and, I suspect, any other ridden entities) go crazy when time-slowed
public static final String NBT_KEY = "time_slowed";
public PotionSlowTime(boolean isBadEffect, int liquidColour){ public PotionSlowTime(boolean isBadEffect, int liquidColour){
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_slow_time.png")); super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_slow_time.png"));
this.setPotionName("potion." + Wizardry.MODID + ":slow_time"); this.setPotionName("potion." + Wizardry.MODID + ":slow_time");
@@ -61,6 +65,8 @@ public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion {
for(Entity entity : targetsInRange){ for(Entity entity : targetsInRange){
entity.getEntityData().setBoolean(NBT_KEY, true);
// If time is stopped, block all updates; otherwise block all updates except every [interval] ticks // If time is stopped, block all updates; otherwise block all updates except every [interval] ticks
entity.updateBlocked = stopTime || host.ticksExisted % interval != 0; entity.updateBlocked = stopTime || host.ticksExisted % interval != 0;
@@ -130,6 +136,29 @@ public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion {
} }
/**
* Goes through every entity in the given world and does the following:<br>
* 1. Checks if they have the slow time NBT tag<br>
* 2. If so, scans the area nearby for players or NPCs with the slow time effect<br>
* 3. If none are found, removes the slow time NBT tag and unblocks the entity's updates
*/
public static void cleanUpEntities(World world){
// Had trouble with accessing loadedTileEntityList from tick events causing random CMEs so I'm making a
// copy of this too just in case
List<Entity> loadedEntityList = new ArrayList<>(world.loadedEntityList);
for(Entity entity : loadedEntityList){
if(entity.getEntityData().getBoolean(NBT_KEY)){
// Currently only players can cast slow time, but you could apply the effect to NPCs with commands
List<EntityLivingBase> nearby = WizardryUtilities.getEntitiesWithinRadius(getEffectRadius(), entity.posX, entity.posY, entity.posZ, entity.world, EntityLivingBase.class);
if(nearby.stream().noneMatch(e -> e.isPotionActive(WizardryPotions.slow_time))){
entity.getEntityData().removeTag(NBT_KEY);
entity.updateBlocked = false;
}
}
}
}
@SubscribeEvent @SubscribeEvent
public static void onLivingUpdateEvent(LivingUpdateEvent event){ public static void onLivingUpdateEvent(LivingUpdateEvent event){
@@ -150,23 +179,16 @@ public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion {
} }
@SubscribeEvent @SubscribeEvent
public static void onPotionExpiryEvent(PotionEvent.PotionExpiryEvent event){ public static void tick(TickEvent.WorldTickEvent event){
if(event.getPotionEffect() != null && event.getPotionEffect().getPotion() == WizardryPotions.slow_time){ if(!event.world.isRemote && event.phase == TickEvent.Phase.END) cleanUpEntities(event.world);
unblockNearbyEntities(event.getEntityLiving());
if(!event.getEntity().world.isRemote){
WizardryPacketHandler.net.sendToDimension(new PacketEndSlowTime.Message(event.getEntityLiving()), event.getEntity().dimension);
}
}
} }
// We still need this as well as tick events because the player hasn't moved anywhere, they just logged out
// In fact, it won't really matter since the tick event fixes it on login anyway, but if the mod is uninstalled or
// something else weird happens...
@SubscribeEvent @SubscribeEvent
public static void onPotionRemoveEvent(PotionEvent.PotionRemoveEvent event){ public static void onPlayerLoggedOutEvent(PlayerEvent.PlayerLoggedOutEvent event){
if(event.getPotionEffect() != null && event.getPotionEffect().getPotion() == WizardryPotions.slow_time){ if(event.player.updateBlocked) event.player.updateBlocked = false;
unblockNearbyEntities(event.getEntityLiving());
if(!event.getEntity().world.isRemote){
WizardryPacketHandler.net.sendToDimension(new PacketEndSlowTime.Message(event.getEntityLiving()), event.getEntity().dimension);
}
}
} }
} }
@@ -5,6 +5,7 @@ import electroblob.wizardry.entity.construct.*;
import electroblob.wizardry.entity.living.*; import electroblob.wizardry.entity.living.*;
import electroblob.wizardry.entity.projectile.*; import electroblob.wizardry.entity.projectile.*;
import electroblob.wizardry.spell.*; import electroblob.wizardry.spell.*;
import net.minecraft.entity.projectile.EntitySnowball;
import net.minecraft.init.MobEffects; import net.minecraft.init.MobEffects;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
@@ -251,7 +252,7 @@ public final class Spells {
}.addProperties(Spell.DAMAGE).soundValues(1, 1.4f, 0.4f)); }.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 SpellThrowable<>("snowball", EntitySnowball::new).npcSelector((e, o) -> o).soundValues(0.5f, 0.4f, 0.2f)); // Let's spare wizards the pain of the snowball spell
registry.register(new Arc()); registry.register(new Arc());
registry.register(new SpellProjectile<>("thunderbolt", EntityThunderbolt::new).addProperties(Spell.DAMAGE, EntityThunderbolt.KNOCKBACK_STRENGTH).soundValues(0.8f, 0.9f, 0.2f)); registry.register(new SpellProjectile<>("thunderbolt", EntityThunderbolt::new).addProperties(Spell.DAMAGE, EntityThunderbolt.KNOCKBACK_STRENGTH).soundValues(0.8f, 0.9f, 0.2f));
registry.register(new SummonZombie()); registry.register(new SummonZombie());
@@ -321,7 +322,7 @@ public final class Spells {
registry.register(new SpellProjectile<>("darkness_orb", EntityDarknessOrb::new).addProperties(Spell.DAMAGE, Spell.EFFECT_DURATION, Spell.EFFECT_STRENGTH).soundValues(0.5f, 0.4f, 0.2f)); registry.register(new SpellProjectile<>("darkness_orb", EntityDarknessOrb::new).addProperties(Spell.DAMAGE, Spell.EFFECT_DURATION, Spell.EFFECT_STRENGTH).soundValues(0.5f, 0.4f, 0.2f));
registry.register(new ShadowWard()); registry.register(new ShadowWard());
registry.register(new Decay()); registry.register(new Decay());
registry.register(new SpellBuff("water_breathing", 0.3f, 0.3f, 1, () -> MobEffects.WATER_BREATHING){ @Override public boolean canBeCastByNPCs(){ return false; } }.soundValues(0.7f, 1.2f, 0.4f)); registry.register(new SpellBuff("water_breathing", 0.3f, 0.3f, 1, () -> MobEffects.WATER_BREATHING).npcSelector((e, o) -> false).soundValues(0.7f, 1.2f, 0.4f));
registry.register(new Tornado()); registry.register(new Tornado());
registry.register(new Glide()); registry.register(new Glide());
registry.register(new SummonSpiritHorse()); registry.register(new SummonSpiritHorse());
@@ -372,7 +373,7 @@ public final class Spells {
registry.register(new Intimidate()); registry.register(new Intimidate());
registry.register(new Banish()); registry.register(new Banish());
registry.register(new SixthSense()); registry.register(new SixthSense());
registry.register(new SpellBuff("darkvision", 0, 0.4f, 0.7f, () -> MobEffects.NIGHT_VISION){ @Override public boolean canBeCastByNPCs(){ return false; } }.soundValues(0.7f, 1.2f, 0.4f)); registry.register(new SpellBuff("darkvision", 0, 0.4f, 0.7f, () -> MobEffects.NIGHT_VISION).npcSelector((e, o) -> false).soundValues(0.7f, 1.2f, 0.4f));
registry.register(new Clairvoyance()); registry.register(new Clairvoyance());
registry.register(new PocketWorkbench()); registry.register(new PocketWorkbench());
registry.register(new ImbueWeapon()); registry.register(new ImbueWeapon());
@@ -5,7 +5,6 @@ import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.loot.RandomSpell; import electroblob.wizardry.loot.RandomSpell;
import electroblob.wizardry.loot.WizardSpell; import electroblob.wizardry.loot.WizardSpell;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EnumCreatureType; import net.minecraft.entity.EnumCreatureType;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.loot.*; import net.minecraft.world.storage.loot.*;
@@ -133,10 +133,17 @@ public final class WizardrySounds {
public static final SoundEvent MISC_PAGE_TURN = createSound("misc.page_turn"); public static final SoundEvent MISC_PAGE_TURN = createSound("misc.page_turn");
public static final SoundEvent MISC_FREEZE = createSound("misc.freeze"); public static final SoundEvent MISC_FREEZE = createSound("misc.freeze");
/** Trick borrowed from the Twilight Forest, makes things neater. */ // Trick borrowed from the Twilight Forest, makes things neater.
/** Overload for {@link WizardrySounds#createSound(String, String)} which assigns wizardry's mod ID automatically. */
public static SoundEvent createSound(String name){ public static SoundEvent createSound(String name){
return createSound(Wizardry.MODID, name);
}
/** Creates a sound with the given name, to be read from {@code assets/[modID]/sounds.json}. */
public static SoundEvent createSound(String modID, String name){
// All the setRegistryName methods delegate to this one, it doesn't matter which you use. // All the setRegistryName methods delegate to this one, it doesn't matter which you use.
return new SoundEvent(new ResourceLocation(Wizardry.MODID, name)).setRegistryName(name); return new SoundEvent(new ResourceLocation(modID, name)).setRegistryName(name);
} }
// For some reason, sound events seem to work even when they aren't registered, without even so much as a warning. // For some reason, sound events seem to work even when they aren't registered, without even so much as a warning.
@@ -251,7 +258,7 @@ public final class WizardrySounds {
event.getRegistry().register(MISC_PAGE_TURN); event.getRegistry().register(MISC_PAGE_TURN);
event.getRegistry().register(MISC_FREEZE); event.getRegistry().register(MISC_FREEZE);
for(Spell spell : Spell.getSpells(Spell.allSpells)){ for(Spell spell : Spell.getAllSpells()){
event.getRegistry().registerAll(spell.getSounds()); event.getRegistry().registerAll(spell.getSounds());
} }
@@ -6,11 +6,13 @@ import electroblob.wizardry.util.NBTExtras;
import electroblob.wizardry.util.SpellModifiers; 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.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@@ -32,9 +34,9 @@ public class ArcaneLock extends SpellRay {
@Override public boolean requiresPacket(){ return true; } @Override public boolean requiresPacket(){ return true; }
@Override public boolean canBeCastByDispensers(){ return false; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser){ return false; }
@Override public boolean canBeCastByNPCs(){ return false; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return false; }
@Override @Override
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){ protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
@@ -136,7 +136,7 @@ public class Blink extends Spell {
} }
@Override @Override
public boolean canBeCastByNPCs(){ public boolean canBeCastBy(EntityLiving npc, boolean override){
return true; return true;
} }
@@ -12,6 +12,7 @@ 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;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.monster.EntityZombie; import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
@@ -21,6 +22,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.pathfinding.Path; import net.minecraft.pathfinding.Path;
import net.minecraft.pathfinding.PathNodeType; import net.minecraft.pathfinding.PathNodeType;
import net.minecraft.pathfinding.PathPoint; import net.minecraft.pathfinding.PathPoint;
import net.minecraft.tileentity.TileEntityDispenser;
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.TextComponentTranslation; import net.minecraft.util.text.TextComponentTranslation;
@@ -44,8 +46,8 @@ public class Clairvoyance extends Spell {
WizardData.registerStoredVariables(LOCATION_KEY, DIMENSION_KEY); WizardData.registerStoredVariables(LOCATION_KEY, DIMENSION_KEY);
} }
@Override public boolean canBeCastByNPCs() { return false; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
@Override public boolean canBeCastByDispensers() { return false; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
@Override @Override
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
@@ -3,6 +3,7 @@ package electroblob.wizardry.spell;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import electroblob.wizardry.item.IConjuredItem; import electroblob.wizardry.item.IConjuredItem;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.util.NBTExtras;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
@@ -40,7 +41,7 @@ public class ConjureArmour extends SpellConjuration {
armour = new ItemStack(SPECTRAL_ARMOUR_MAP.get(slot)); armour = new ItemStack(SPECTRAL_ARMOUR_MAP.get(slot));
IConjuredItem.setDurationMultiplier(armour, modifiers.get(WizardryItems.duration_upgrade)); IConjuredItem.setDurationMultiplier(armour, modifiers.get(WizardryItems.duration_upgrade));
// Sets a blank "ench" tag to trick the renderer into showing the enchantment effect on the armour model // Sets a blank "ench" tag to trick the renderer into showing the enchantment effect on the armour model
armour.getTagCompound().setTag("ench", new NBTTagList()); NBTExtras.storeTagSafely(armour.getTagCompound(), "ench", new NBTTagList());
caster.setItemStackToSlot(slot, armour); caster.setItemStackToSlot(slot, armour);
flag = true; flag = true;
} }
@@ -9,12 +9,14 @@ import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.util.*; import electroblob.wizardry.util.*;
import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.DamageSource; import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -45,9 +47,9 @@ public class CurseOfSoulbinding extends SpellRay {
WizardData.registerStoredVariables(TARGETS_KEY); WizardData.registerStoredVariables(TARGETS_KEY);
} }
@Override public boolean canBeCastByNPCs() { return false; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
// You can't damage a dispenser so this would be nonsense! // You can't damage a dispenser so this would be nonsense!
@Override public boolean canBeCastByDispensers() { return false; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
@Override @Override
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){ protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
@@ -21,7 +21,7 @@ public class Decoy extends Spell {
addProperties(DECOY_LIFETIME, MOB_TRICK_CHANCE); addProperties(DECOY_LIFETIME, MOB_TRICK_CHANCE);
} }
@Override public boolean canBeCastByNPCs(){ return true; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return true; }
@Override @Override
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Settings;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockCrystalOre; import electroblob.wizardry.block.BlockCrystalOre;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
@@ -21,6 +22,7 @@ import net.minecraft.util.math.BlockPos;
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 org.apache.commons.lang3.tuple.Pair;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
@@ -65,11 +67,13 @@ public class Divination extends Spell {
List<BlockPos> sphere = WizardryUtilities.getBlockSphere(caster.getPosition(), range); List<BlockPos> sphere = WizardryUtilities.getBlockSphere(caster.getPosition(), range);
sphere.removeIf(b -> !(world.getBlockState(b).getBlock() instanceof BlockOre sphere.removeIf(b -> {
|| world.getBlockState(b).getBlock() instanceof BlockRedstoneOre Block block = world.getBlockState(b).getBlock();
|| world.getBlockState(b).getBlock() instanceof BlockCrystalOre return !(block instanceof BlockOre
|| Arrays.asList(Wizardry.settings.divinationOreWhitelist) || block instanceof BlockRedstoneOre
.contains(world.getBlockState(b).getBlock().getRegistryName()))); || block instanceof BlockCrystalOre
|| Settings.containsMetaBlock(Wizardry.settings.divinationOreWhitelist, world.getBlockState(b)));
});
Strength strength = Strength.NOTHING; Strength strength = Strength.NOTHING;
@@ -85,7 +85,7 @@ public class DragonFireball extends Spell {
} }
@Override @Override
public boolean canBeCastByNPCs(){ public boolean canBeCastBy(EntityLiving npc, boolean override){
return true; return true;
} }
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.construct.EntityEarthquake; import electroblob.wizardry.entity.construct.EntityEarthquake;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
@@ -7,6 +8,7 @@
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
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;
@@ -53,6 +55,10 @@
world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, y, world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, y,
particleZ, particleX - x, 0, particleZ - z, Block.getStateId(block)); particleZ, particleX - x, 0, particleZ - z, Block.getStateId(block));
} }
WizardryUtilities.getEntitiesWithinRadius(15, x, y, z, world, EntityPlayer.class)
.forEach(p -> Wizardry.proxy.shakeScreen(p, 12));
} }
return super.spawnConstruct(world, x, y, z, side, caster, modifiers); return super.spawnConstruct(world, x, y, z, side, caster, modifiers);
@@ -75,7 +75,8 @@ public class EmpoweringPresence extends Spell {
@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.
if(event.getCaster() != null && event.getCaster().isPotionActive(WizardryPotions.empowerment)){ if(event.getCaster() != null && event.getCaster().isPotionActive(WizardryPotions.empowerment)
&& !(event.getSpell() instanceof EmpoweringPresence)){ // Prevent exponential empowerment stacking!
float potency = 1 + Constants.EMPOWERMENT_POTENCY_PER_LEVEL float potency = 1 + Constants.EMPOWERMENT_POTENCY_PER_LEVEL
* (event.getCaster().getActivePotionEffect(WizardryPotions.empowerment).getAmplifier() + 1); * (event.getCaster().getActivePotionEffect(WizardryPotions.empowerment).getAmplifier() + 1);
@@ -31,7 +31,7 @@ public class FlamingWeapon extends Spell {
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){ for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){
if((ImbueWeapon.isSword(stack.getItem()) || ImbueWeapon.isBow(stack.getItem())) if((ImbueWeapon.isSword(stack) || ImbueWeapon.isBow(stack))
&& !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.flaming_weapon)){ && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.flaming_weapon)){
// The enchantment level as determined by the damage multiplier. The + 0.5f is so that // The enchantment level as determined by the damage multiplier. The + 0.5f is so that
// weird float processing doesn't incorrectly round it down. // weird float processing doesn't incorrectly round it down.
@@ -11,6 +11,7 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -29,8 +30,8 @@ public class ForestOfThorns extends Spell {
} }
@Override public boolean requiresPacket(){ return false; } @Override public boolean requiresPacket(){ return false; }
@Override public boolean canBeCastByNPCs(){ return true; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return true; }
@Override public boolean canBeCastByDispensers(){ return true; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser){ return true; }
@Override @Override
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
@@ -37,7 +37,7 @@ public class FreezingWeapon extends Spell {
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){ for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){
if((ImbueWeapon.isSword(stack.getItem()) || ImbueWeapon.isBow(stack.getItem())) if((ImbueWeapon.isSword(stack) || ImbueWeapon.isBow(stack))
&& !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.freezing_weapon)){ && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.freezing_weapon)){
// The enchantment level as determined by the damage multiplier. The + 0.5f is so that // The enchantment level as determined by the damage multiplier. The + 0.5f is so that
// weird float processing doesn't incorrectly round it down. // weird float processing doesn't incorrectly round it down.
@@ -9,7 +9,6 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; 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.SoundEvent;
import net.minecraft.world.World; import net.minecraft.world.World;
public class Glide extends Spell { public class Glide extends Spell {
@@ -19,6 +19,7 @@ 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.network.play.server.SPacketEntityVelocity; import net.minecraft.network.play.server.SPacketEntityVelocity;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundEvent; import net.minecraft.util.SoundEvent;
@@ -58,12 +59,12 @@ public class Grapple extends Spell {
} }
@Override @Override
public boolean canBeCastByNPCs(){ public boolean canBeCastBy(EntityLiving npc, boolean override){
return super.canBeCastByNPCs(); return true;
} }
@Override @Override
public boolean canBeCastByDispensers(){ public boolean canBeCastBy(TileEntityDispenser dispenser){
return true; return true;
} }
@@ -9,12 +9,14 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
import electroblob.wizardry.util.SpellModifiers; 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.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityTNTPrimed; import net.minecraft.entity.item.EntityTNTPrimed;
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.network.play.server.SPacketEntityVelocity; import net.minecraft.network.play.server.SPacketEntityVelocity;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.SoundEvent; import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -41,8 +43,8 @@ public class GreaterTelekinesis extends SpellRay {
this.soundValues(0.8f, 1, 0.2f); this.soundValues(0.8f, 1, 0.2f);
} }
@Override public boolean canBeCastByNPCs() { return false; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
@Override public boolean canBeCastByDispensers() { return false; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
@Override @Override
protected SoundEvent[] createSounds(){ protected SoundEvent[] createSounds(){
@@ -4,6 +4,7 @@ import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.SpellModifiers; 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.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
@@ -19,6 +20,11 @@ public class HealAlly extends SpellRay {
addProperties(HEALTH); addProperties(HEALTH);
} }
@Override
public boolean canBeCastBy(EntityLiving npc, boolean override){
return false;
}
@Override @Override
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){ protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Settings;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Constants; import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.data.WizardData; import electroblob.wizardry.data.WizardData;
@@ -11,9 +12,13 @@ import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.*; import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemBow;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Arrays; import java.util.Arrays;
@@ -32,7 +37,7 @@ public class ImbueWeapon extends Spell {
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){ for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){
if(isSword(stack.getItem()) if(isSword(stack)
&& !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_sword) && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_sword)
&& WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_sword) <= 0){ && WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_sword) <= 0){
// The enchantment level as determined by the damage multiplier. The + 0.5f is so that // The enchantment level as determined by the damage multiplier. The + 0.5f is so that
@@ -44,7 +49,7 @@ public class ImbueWeapon extends Spell {
WizardData.get(caster).setImbuementDuration(WizardryEnchantments.magic_sword, WizardData.get(caster).setImbuementDuration(WizardryEnchantments.magic_sword,
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade))); (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
}else if(isBow(stack.getItem()) }else if(isBow(stack)
&& !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_bow) && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_bow)
&& WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_bow) <= 0){ && WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_bow) <= 0){
// The enchantment level as determined by the damage multiplier. The + 0.5f is so that // The enchantment level as determined by the damage multiplier. The + 0.5f is so that
@@ -77,13 +82,13 @@ public class ImbueWeapon extends Spell {
} }
/** Returns true if the given item counts as a sword, i.e. it extends {@link ItemSword} or is in the whitelist. */ /** Returns true if the given item counts as a sword, i.e. it extends {@link ItemSword} or is in the whitelist. */
public static boolean isSword(Item item){ public static boolean isSword(ItemStack stack){
return item instanceof ItemSword || Arrays.asList(Wizardry.settings.swordItemWhitelist).contains(item.getRegistryName()); return stack.getItem() instanceof ItemSword || Settings.containsMetaItem(Wizardry.settings.swordItemWhitelist, stack);
} }
/** Returns true if the given item counts as a bow, i.e. it extends {@link ItemBow} or is in the whitelist. */ /** Returns true if the given item counts as a bow, i.e. it extends {@link ItemBow} or is in the whitelist. */
public static boolean isBow(Item item){ public static boolean isBow(ItemStack stack){
return item instanceof ItemBow || Arrays.asList(Wizardry.settings.bowItemWhitelist).contains(item.getRegistryName()); return stack.getItem() instanceof ItemBow || Settings.containsMetaItem(Wizardry.settings.bowItemWhitelist, stack);
} }
} }
@@ -10,6 +10,7 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
import electroblob.wizardry.util.SpellModifiers; 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.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.monster.*; import net.minecraft.entity.monster.*;
import net.minecraft.entity.passive.*; import net.minecraft.entity.passive.*;
@@ -52,7 +53,7 @@ public class Metamorphosis extends SpellRay {
this.soundValues(0.5f, 1f, 0); this.soundValues(0.5f, 1f, 0);
} }
@Override public boolean canBeCastByNPCs() { return false; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
@Override @Override
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){ protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
@@ -19,6 +19,7 @@ import net.minecraft.item.EnumAction;
import net.minecraft.item.EnumDyeColor; import net.minecraft.item.EnumDyeColor;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@@ -43,8 +44,8 @@ public class MindControl extends SpellRay {
addProperties(EFFECT_DURATION); addProperties(EFFECT_DURATION);
} }
@Override public boolean canBeCastByNPCs() { return false; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
@Override public boolean canBeCastByDispensers() { return false; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
@Override @Override
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){ protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
@@ -1,5 +1,6 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.Settings;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
@@ -10,8 +11,6 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.EnumParticleTypes;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.Arrays;
public class PocketFurnace extends Spell { public class PocketFurnace extends Spell {
public static final String ITEMS_SMELTED = "items_smelted"; public static final String ITEMS_SMELTED = "items_smelted";
@@ -37,9 +36,9 @@ public class PocketFurnace extends Spell {
result = FurnaceRecipes.instance().getSmeltingResult(stack); result = FurnaceRecipes.instance().getSmeltingResult(stack);
if(!result.isEmpty() && !(result.getItem() instanceof ItemTool) && !(result.getItem() instanceof ItemSword) if(!result.isEmpty() && !(stack.getItem() instanceof ItemTool) && !(stack.getItem() instanceof ItemSword)
&& !(result.getItem() instanceof ItemArmor) && !(stack.getItem() instanceof ItemArmor)
&& !Arrays.asList(Wizardry.settings.pocketFurnaceItemBlacklist).contains(result.getItem().getRegistryName())){ && !Settings.containsMetaItem(Wizardry.settings.pocketFurnaceItemBlacklist, stack)){
if(stack.getCount() <= usesLeft){ if(stack.getCount() <= usesLeft){
ItemStack stack2 = new ItemStack(result.getItem(), stack.getCount(), result.getItemDamage()); ItemStack stack2 = new ItemStack(result.getItem(), stack.getCount(), result.getItemDamage());
@@ -8,6 +8,7 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
import electroblob.wizardry.util.SpellModifiers; 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.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.MobEffects; import net.minecraft.init.MobEffects;
@@ -28,7 +29,7 @@ public class Poison extends SpellRay {
} }
@Override @Override
public boolean canBeCastByNPCs(){ public boolean canBeCastBy(EntityLiving npc, boolean override){
return true; return true;
} }
@@ -16,6 +16,7 @@ import electroblob.wizardry.packet.PacketPossession;
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.WizardryItems;
import electroblob.wizardry.util.NBTExtras;
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;
@@ -43,6 +44,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagList;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.potion.PotionUtils; import net.minecraft.potion.PotionUtils;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.*; import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -123,8 +125,8 @@ public class Possession extends SpellRay {
addProperties(EFFECT_DURATION, CRITICAL_HEALTH); addProperties(EFFECT_DURATION, CRITICAL_HEALTH);
} }
@Override public boolean canBeCastByNPCs() { return false; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
@Override public boolean canBeCastByDispensers() { return false; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
@Override @Override
public boolean requiresPacket(){ public boolean requiresPacket(){
@@ -199,6 +201,8 @@ public class Possession extends SpellRay {
*/ */
public boolean possess(EntityPlayer possessor, EntityLiving target, int duration){ public boolean possess(EntityPlayer possessor, EntityLiving target, int duration){
if(possessor.isSneaking()) return false;
if(WizardData.get(possessor) != null){ if(WizardData.get(possessor) != null){
WizardData.get(possessor).setVariable(POSSESSEE_KEY, target); WizardData.get(possessor).setVariable(POSSESSEE_KEY, target);
@@ -267,7 +271,7 @@ public class Possession extends SpellRay {
// Inventory and items // Inventory and items
if(possessor.getEntityData() != null){ if(possessor.getEntityData() != null){
possessor.getEntityData().setTag(INVENTORY_NBT_KEY, possessor.inventory.writeToNBT(new NBTTagList())); NBTExtras.storeTagSafely(possessor.getEntityData(), INVENTORY_NBT_KEY, possessor.inventory.writeToNBT(new NBTTagList()));
} }
possessor.inventory.clear(); possessor.inventory.clear();
@@ -681,11 +685,13 @@ public class Possession extends SpellRay {
if(possessee != null){ if(possessee != null){
if(possessee.canPickUpLoot() && possessee.getHeldItemMainhand().isEmpty()){ event.setCanceled(true);
possessee.setHeldItem(EnumHand.MAIN_HAND, event.getItem().getItem());
}else{ // if(possessee.canPickUpLoot() && possessee.getHeldItemMainhand().isEmpty()){
event.setCanceled(true); // possessee.setHeldItem(EnumHand.MAIN_HAND, event.getItem().getItem());
} // }else{
// event.setCanceled(true);
// }
} }
} }
@@ -1,6 +1,7 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
@@ -17,7 +18,7 @@ public class ReplenishHunger extends SpellBuff {
addProperties(HUNGER_POINTS, SATURATION_MODIFIER); addProperties(HUNGER_POINTS, SATURATION_MODIFIER);
} }
@Override public boolean canBeCastByNPCs(){ return false; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return false; }
@Override @Override
protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){ protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){
@@ -25,6 +25,8 @@ public class Resurrection extends Spell {
public static final String WAIT_TIME = "wait_time"; public static final String WAIT_TIME = "wait_time";
private static final String POTION_CORE_FIX_NBT_KEY = "Potion Core - Health Fix";
public Resurrection(){ public Resurrection(){
super("resurrection", EnumAction.NONE, false); super("resurrection", EnumAction.NONE, false);
addProperties(EFFECT_RADIUS, WAIT_TIME); addProperties(EFFECT_RADIUS, WAIT_TIME);
@@ -90,6 +92,8 @@ public class Resurrection extends Spell {
player.experience = 0; player.experience = 0;
player.experienceLevel = 0; player.experienceLevel = 0;
player.experienceTotal = 0; player.experienceTotal = 0;
// Not sure what potion core is 'fixing' but it breaks my resurrection, so let's unfix it!
player.getEntityData().removeTag(POTION_CORE_FIX_NBT_KEY);
if(player.world.isRemote){ if(player.world.isRemote){
ParticleBuilder.spawnHealParticles(player.world, player); ParticleBuilder.spawnHealParticles(player.world, player);
@@ -8,6 +8,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@@ -28,7 +29,7 @@ public class Reversal extends SpellRay {
} }
@Override @Override
public boolean canBeCastByDispensers(){ public boolean canBeCastBy(TileEntityDispenser dispenser){
return false; return false;
} }
@@ -1,6 +1,7 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
@@ -17,7 +18,7 @@ public class Satiety extends SpellBuff {
addProperties(HUNGER_POINTS, SATURATION_MODIFIER); addProperties(HUNGER_POINTS, SATURATION_MODIFIER);
} }
@Override public boolean canBeCastByNPCs(){ return false; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return false; }
@Override @Override
protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){ protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){
@@ -41,12 +41,17 @@ public class Shockwave extends Spell {
for(EntityLivingBase target : targets){ for(EntityLivingBase target : targets){
if(target instanceof EntityPlayer && (!Wizardry.settings.playersMoveEachOther if(target instanceof EntityPlayer){
|| ItemArtefact.isArtefactActive((EntityPlayer)target, WizardryItems.amulet_anchoring))){
if(!world.isRemote) caster.sendStatusMessage(new TextComponentTranslation("spell.resist", Wizardry.proxy.shakeScreen((EntityPlayer)target, 10);
target.getName(), this.getNameForTranslationFormatted()), true);
return false; if(!Wizardry.settings.playersMoveEachOther) continue;
if(ItemArtefact.isArtefactActive((EntityPlayer)target, WizardryItems.amulet_anchoring)){
if(!world.isRemote) caster.sendStatusMessage(new TextComponentTranslation("spell.resist",
target.getName(), this.getNameForTranslationFormatted()), true);
continue;
}
} }
if(AllyDesignationSystem.isValidTarget(caster, target)){ if(AllyDesignationSystem.isValidTarget(caster, target)){
@@ -1,6 +1,7 @@
package electroblob.wizardry.spell; package electroblob.wizardry.spell;
import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.util.NBTExtras;
import electroblob.wizardry.util.SpellModifiers; 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;
@@ -12,6 +13,7 @@ import net.minecraft.entity.projectile.EntityShulkerBullet;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -29,9 +31,9 @@ public class ShulkerBullet extends Spell {
addProperties(RANGE); addProperties(RANGE);
} }
@Override public boolean canBeCastByNPCs(){ return true; } @Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return true; }
@Override public boolean canBeCastByDispensers(){ return true; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser){ return true; }
@Override @Override
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
@@ -89,7 +91,7 @@ public class ShulkerBullet extends Spell {
targetTag.setInteger("X", pos.getX()); targetTag.setInteger("X", pos.getX());
targetTag.setInteger("Y", pos.getY()); targetTag.setInteger("Y", pos.getY());
targetTag.setInteger("Z", pos.getZ()); targetTag.setInteger("Z", pos.getZ());
nbt.setTag("Target", targetTag); NBTExtras.storeTagSafely(nbt, "Target", targetTag);
bullet.readFromNBT(nbt); // LOL I just modified private fields without reflection bullet.readFromNBT(nbt); // LOL I just modified private fields without reflection
world.spawnEntity(bullet); world.spawnEntity(bullet);
@@ -3,7 +3,9 @@ package electroblob.wizardry.spell;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World; import net.minecraft.world.World;
@@ -35,12 +37,12 @@ public class SlowTime extends SpellBuff {
} }
@Override @Override
public boolean canBeCastByDispensers(){ public boolean canBeCastBy(TileEntityDispenser dispenser){
return false; return false;
} }
@Override @Override
public boolean canBeCastByNPCs(){ public boolean canBeCastBy(EntityLiving npc, boolean override){
return false; return false;
} }
} }
@@ -1,45 +0,0 @@
package electroblob.wizardry.spell;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntitySnowball;
import net.minecraft.item.EnumAction;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
public class Snowball extends Spell {
public Snowball(){
super("snowball", EnumAction.NONE, false);
addProperties(RANGE);
soundValues(0.5f, 0.4f, 0.2f);
}
@Override
public boolean requiresPacket(){
return false;
}
@Override
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
if(!world.isRemote){
// Trajectory calculation - see SpellProjectile for a more detailed explanation
float g = 0.03f;
float launchHeight = caster.getEyeHeight();
float range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade);
float velocity = MathHelper.sqrt(MathHelper.sqrt(g*g * (launchHeight*launchHeight + range*range)) - g*launchHeight);
EntitySnowball snowball = new EntitySnowball(world, caster);
snowball.shoot(caster, caster.rotationPitch, caster.rotationYaw, 0.0f, velocity, 1.0f);
world.spawnEntity(snowball);
}
this.playSound(world, caster, ticksInUse, -1, modifiers);
caster.swingArm(hand);
return true;
}
}
@@ -20,6 +20,7 @@ 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.item.Item;
import net.minecraft.tileentity.TileEntityDispenser;
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;
@@ -42,6 +43,7 @@ import net.minecraftforge.registries.IForgeRegistryEntry;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -149,6 +151,17 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
/** The pitch variation of the sound played when this spell is cast. Defaults to 0. */ /** The pitch variation of the sound played when this spell is cast. Defaults to 0. */
protected float pitchVariation = 0; protected float pitchVariation = 0;
// The following two fields are supposed to eliminate the need for boilerplate classes in spell packs.
// As an example, spells in the twilight forest spell pack only appear on custom spell book and scroll items and
// NPCs can only cast them if they spawned in the twilight forest. Without these convenience fields, spells that
// would not otherwise require their own classes have to have one just so they can override those two behaviours.
/** List of items for which this spell is applicable (used by default behaviour of {@link Spell#applicableForItem(Item)}). */
protected Item[] applicableItems;
/** Predicate that specifies a condition that NPCs must satisfy in order to spawn with this spell equipped (used by
* default behaviour of {@link Spell#canBeCastBy(EntityLiving, boolean)}). */
protected BiPredicate<EntityLiving, Boolean> npcSelector; // Kinda ugly but it's better than boilerplate classes
private static int nextSpellId = 0; private static int nextSpellId = 0;
/** The spell's integer ID, mainly used for networking. */ /** The spell's integer ID, mainly used for networking. */
// This was added after I learnt the hard way why you can't assume Forge's registry IDs are sequential... // This was added after I learnt the hard way why you can't assume Forge's registry IDs are sequential...
@@ -186,6 +199,8 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
this.icon = new ResourceLocation(modID, "textures/spells/" + name + ".png"); this.icon = new ResourceLocation(modID, "textures/spells/" + name + ".png");
this.sounds = createSounds(); this.sounds = createSounds();
this.id = nextSpellId++; this.id = nextSpellId++;
this.items(WizardryItems.spell_book, WizardryItems.scroll);
this.npcSelector((e, o) -> canBeCastByNPCs()); // Fallback to old behaviour until we remove it entirely
} }
// ========================================= Initialisation methods =========================================== // ========================================= Initialisation methods ===========================================
@@ -204,7 +219,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
* @see Spell#playSound(World, double, double, double, int, int, SpellModifiers, String...) * @see Spell#playSound(World, double, double, double, int, int, SpellModifiers, String...)
*/ */
protected SoundEvent[] createSounds(){ protected SoundEvent[] createSounds(){
return new SoundEvent[]{WizardrySounds.createSound("spell." + this.getRegistryName().getPath())}; return new SoundEvent[]{WizardrySounds.createSound(this.getRegistryName().getNamespace(), "spell." + this.getRegistryName().getPath())};
} }
// Note 1: The aim here is conciseness. Keeping the identifiers in the spell classes means we don't usually have to // Note 1: The aim here is conciseness. Keeping the identifiers in the spell classes means we don't usually have to
@@ -294,7 +309,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
* @see Spell#createSoundsWithSuffixes(String[]) * @see Spell#createSoundsWithSuffixes(String[])
*/ */
public final SoundEvent createSoundWithSuffix(String suffix){ public final SoundEvent createSoundWithSuffix(String suffix){
return WizardrySounds.createSound("spell." + this.getRegistryName().getPath() + "." + suffix); return WizardrySounds.createSound(this.getRegistryName().getNamespace(), "spell." + this.getRegistryName().getPath() + "." + suffix);
} }
/** /**
@@ -354,7 +369,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
* work if the caster is on full health). * work if the caster is on full health).
* <p></p> * <p></p>
* This method is intended for use by NPCs (see {@link EntityWizard}) so that they can cast spells. Override it if * This method is intended for use by NPCs (see {@link EntityWizard}) so that they can cast spells. Override it if
* you want a spell to be cast by wizards. Note that you must also override {@link Spell#canBeCastByNPCs()} to * you want a spell to be cast by wizards. Note that you must also override {@link Spell#canBeCastBy(EntityLiving, boolean)} to
* return true to allow wizards to select the spell. For some spells, this method may well be exactly the same as * return true to allow wizards to select the spell. For some spells, this method may well be exactly the same as
* the regular cast method; for others it won't be - for example, projectile-based spells are normally done using * the regular cast method; for others it won't be - for example, projectile-based spells are normally done using
* the player's look vector, but NPCs need to use a target-based method instead. * the player's look vector, but NPCs need to use a target-based method instead.
@@ -390,7 +405,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
* won't work if the caster is on full health). * won't work if the caster is on full health).
* <p></p> * <p></p>
* This method is intended for use by dispensers and command blocks so that they can cast spells. Override it if * This method is intended for use by dispensers and command blocks so that they can cast spells. Override it if
* you want a spell to be cast by dispensers. Note that you must also override {@link Spell#canBeCastByDispensers()} to * you want a spell to be cast by dispensers. Note that you must also override {@link Spell#canBeCastBy(TileEntityDispenser)} to
* return true to allow dispensers to select the spell. For some spells, this method may well be exactly the same as * return true to allow dispensers to select the spell. For some spells, this method may well be exactly the same as
* the regular cast method; for others it won't be - for example, projectile-based spells are normally done using * the regular cast method; for others it won't be - for example, projectile-based spells are normally done using
* the player's look vector, but dispensers need to use a facing-based method instead. * the player's look vector, but dispensers need to use a facing-based method instead.
@@ -446,20 +461,50 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
public void finishCasting(World world, @Nullable EntityLivingBase caster, double x, double y, double z, public void finishCasting(World world, @Nullable EntityLivingBase caster, double x, double y, double z,
@Nullable EnumFacing direction, int duration, SpellModifiers modifiers){} @Nullable EnumFacing direction, int duration, SpellModifiers modifiers){}
/**
* Whether the given entity can cast this spell. If you have overridden
* {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, you should override
* this to return true (either always or under certain circumstances), or alternatively assign an NPC selector via
* {@link Spell#npcSelector(BiPredicate)} (recommended for general spell classes).
* @param npc The entity to query.
* @param override True if a player in creative mode is assigning this spell to the given entity, false otherwise.
* Usually this means situational conditions should be ignored.
*/
// We could make this final and force everyone to move over to the predicate system, but for particularly complex
// behaviour (i.e. several lines of code) it gets too ugly, and then you end up moving the contents of the predicate
// to a static method anyway and referring to it via method reference... so we may as well leave people the option.
public boolean canBeCastBy(EntityLiving npc, boolean override){
return npcSelector.test(npc, override);
}
/** /**
* Whether NPCs such as wizards can cast this spell. If you have overridden * Whether NPCs such as wizards can cast this spell. If you have overridden
* {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, you should override * {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, you should override
* this to return true. * this to return true.
* @deprecated Use the entity-sensitive version {@link Spell#canBeCastBy(EntityLiving, boolean)}.
*/ */
@Deprecated
public boolean canBeCastByNPCs(){ public boolean canBeCastByNPCs(){
return false; return false;
} }
/**
* Whether the given dispenser can cast this spell. If you have overridden
* {@link Spell#cast(World, double, double, double, EnumFacing, int, int, SpellModifiers)}, you should override this
* to return true (either always or under certain circumstances).
* @param dispenser The dispenser to query.
*/
public boolean canBeCastBy(TileEntityDispenser dispenser){
return canBeCastByDispensers();
}
/** /**
* Whether dispensers can cast this spell. If you have overridden * Whether dispensers can cast this spell. If you have overridden
* {@link Spell#cast(World, double, double, double, EnumFacing, int, int, SpellModifiers)}, you should override this * {@link Spell#cast(World, double, double, double, EnumFacing, int, int, SpellModifiers)}, you should override this
* to return true. * to return true.
* @deprecated Use the tileentity-sensitive version {@link Spell#canBeCastBy(TileEntityDispenser)}.
*/ */
@Deprecated
public boolean canBeCastByDispensers(){ public boolean canBeCastByDispensers(){
return false; return false;
} }
@@ -561,26 +606,6 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
return properties.getBaseValue(identifier); return properties.getBaseValue(identifier);
} }
/** Returns whether the spell is enabled in any of the given {@link electroblob.wizardry.util.SpellProperties.Context Context}s.
* A spell may be disabled globally in the config, or it may be disabled for one or more specific contexts in
* its JSON file using a resource pack. If called with no arguments, defaults to any context, i.e. only returns
* false if the spell is completely disabled in all contexts. */
public final boolean isEnabled(SpellProperties.Context... contexts){
return enabled && (contexts.length == 0 || properties.isEnabled(contexts));
}
/** Sets whether the spell is enabled or not. */
public final void setEnabled(boolean 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>
@@ -767,6 +792,51 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
} }
} }
// ============================================= Misc methods ===============================================
/** Returns whether the spell is enabled in any of the given {@link electroblob.wizardry.util.SpellProperties.Context Context}s.
* A spell may be disabled globally in the config, or it may be disabled for one or more specific contexts in
* its JSON file using a resource pack. If called with no arguments, defaults to any context, i.e. only returns
* false if the spell is completely disabled in all contexts. */
public final boolean isEnabled(SpellProperties.Context... contexts){
return enabled && (contexts.length == 0 || properties.isEnabled(contexts));
}
/** Sets whether the spell is enabled or not. */
public final void setEnabled(boolean 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
* in this spell's {@link Spell#applicableItems} list (set using {@link Spell#items(Item...)}). Override to do
* something more complex. */
public boolean applicableForItem(Item item){
return Arrays.asList(applicableItems).contains(item);
}
/**
* Sets which items this spell can appear on (these default to the regular spell book and scroll).
* @param applicableItems The items this spell should naturally appear on (or no items at all).
* @return The spell instance, allowing this method to be chained onto the constructor. Note that since this method
* only returns a {@code Spell}, if you are chaining multiple methods onto the constructor this should be called last.
*/
public Spell items(Item... applicableItems){
this.applicableItems = applicableItems;
return this;
}
/**
* Specifies a condition that NPCs must satisfy in order to spawn with this spell equipped (this defaults to always
* true).
* @param selector A condition that NPCs must satisfy in order to spawn with this spell equipped.
* @return The spell instance, allowing this method to be chained onto the constructor. Note that since this method
* only returns a {@code Spell}, if you are chaining multiple methods onto the constructor this should be called last.
*/
public Spell npcSelector(BiPredicate<EntityLiving, Boolean> selector){
this.npcSelector = selector;
return this;
}
// Spells are sorted according to tier and element. Where several spells have the same tier and element, // Spells are sorted according to tier and element. Where several spells have the same tier and element,
// they will remain in the order they were registered. // they will remain in the order they were registered.
@Override @Override
@@ -785,7 +855,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
/** /**
* Returns the total number of registered spells, excluding the 'None' spell. Returns the same number that would be * Returns the total number of registered spells, excluding the 'None' spell. Returns the same number that would be
* returned by {@code Spell.getSpells(Spell.allSpells).size()}, but this method is more efficient. * returned by {@code Spell.getAllSpells().size()}, but this method is more efficient.
*/ */
public static int getTotalSpellCount(){ public static int getTotalSpellCount(){
return registry.getValuesCollection().size() - 1; return registry.getValuesCollection().size() - 1;
@@ -833,31 +903,37 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
/** /**
* Returns a list containing all spells matching the given {@link Predicate}. The returned list is separate from the * Returns a list containing all spells matching the given {@link Predicate}. The returned list is separate from the
* internal spells list; any changes you make to the returned list will have no effect on wizardry since the * internal spells list; any changes you make to the returned list will have no effect on wizardry since the
* returned list is local to this method. Never includes the {@link None} spell. For convenience, there are some * returned list is local to this method. Never includes the {@link None} spell.
* predefined predicates in the Spell class (some of these really aren't shortcuts any more):
* <p></p>
* {@link Spell#allSpells} will allow all spells to be returned<br>
* {@link Spell#npcSpells} will only allow enabled spells that can be cast by NPCs (see
* {@link Spell#canBeCastByNPCs()})<br>
* {@link Spell#nonContinuousSpells} will filter out continuous spells but not disabled spells<br>
* {@link TierElementFilter} will only allow enabled spells of the specified tier and element
* *
* @param filter A <code>Predicate&ltSpell&gt</code> that the returned spells must satisfy. * @param filter A <code>Predicate&ltSpell&gt</code> that the returned spells must satisfy.
* *
* @return A <b>local, modifiable</b> list of spells matching the given predicate. <i>Note that this list may be * @return A <b>local, modifiable</b> list of spells matching the given predicate. <i>Note that this list may be
* empty.</i> * empty.</i>
*
* @see TierElementFilter
*/ */
public static List<Spell> getSpells(Predicate<Spell> filter){ public static List<Spell> getSpells(Predicate<Spell> filter){
return registry.getValuesCollection().stream().filter(filter.and(s -> s != Spells.none)).collect(Collectors.toList()); return registry.getValuesCollection().stream().filter(filter.and(s -> s != Spells.none)).collect(Collectors.toList());
} }
/** Predicate which allows all spells. */ /** Returns all registered spells, except the {@link None} spell. */
public static List<Spell> getAllSpells(){
return getSpells(s -> true);
}
/** Predicate which allows all spells.
* @deprecated Use {@link Spell#getAllSpells()}. */
@Deprecated
public static Predicate<Spell> allSpells = s -> true; public static Predicate<Spell> allSpells = s -> true;
/** Predicate which allows all non-continuous spells, even those that have been disabled. */ /** Predicate which allows all non-continuous spells, even those that have been disabled.
* @deprecated Nobody ever uses this now we have continuous scrolls, if you really need it just use a lambda. */
@Deprecated
public static Predicate<Spell> nonContinuousSpells = s -> !s.isContinuous; public static Predicate<Spell> nonContinuousSpells = s -> !s.isContinuous;
/** Predicate which allows all enabled spells for which {@link Spell#canBeCastByNPCs()} returns true. */ /** Predicate which allows all enabled spells for which {@link Spell#canBeCastBy(EntityLiving, boolean)} returns true.
* @deprecated in favour of entity-sensitive version, use a lambda expression directly. */
@Deprecated
public static Predicate<Spell> npcSpells = s -> s.isEnabled(SpellProperties.Context.NPCS) && s.canBeCastByNPCs(); public static Predicate<Spell> npcSpells = s -> s.isEnabled(SpellProperties.Context.NPCS) && s.canBeCastByNPCs();
/** /**
@@ -11,6 +11,7 @@ import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@@ -29,9 +30,9 @@ import java.util.function.Function;
* <p></p> * <p></p>
* Properties added by this type of spell: {@link Spell#RANGE} * Properties added by this type of spell: {@link Spell#RANGE}
* <p></p> * <p></p>
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()} * By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
* <p></p> * <p></p>
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()} * By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
* <p></p> * <p></p>
* By default, this type of spell does not require a packet to be sent. {@link Spell#requiresPacket()} * By default, this type of spell does not require a packet to be sent. {@link Spell#requiresPacket()}
* *
@@ -58,13 +59,12 @@ public class SpellArrow<T extends EntityMagicArrow> extends Spell {
super(modID, name, EnumAction.NONE, false); super(modID, name, EnumAction.NONE, false);
this.arrowFactory = arrowFactory; this.arrowFactory = arrowFactory;
this.addProperties(RANGE); this.addProperties(RANGE);
this.npcSelector((e, o) -> true);
} }
@Override public boolean requiresPacket(){ return false; } @Override public boolean requiresPacket(){ return false; }
@Override public boolean canBeCastByNPCs(){ return true; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return true; }
@Override public boolean canBeCastByDispensers() { return true; }
/** Computes the velocity the projectile should be launched at to achieve the required range. */ /** Computes the velocity the projectile should be launched at to achieve the required range. */
// Long story short, it doesn't make much sense to me to have the JSON file specify the velocity - even less so if // Long story short, it doesn't make much sense to me to have the JSON file specify the velocity - even less so if
@@ -11,6 +11,7 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction; import net.minecraft.item.EnumAction;
import net.minecraft.potion.Potion; import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
@@ -32,9 +33,9 @@ import java.util.stream.Collectors;
* <p></p> * <p></p>
* Properties added by this type of spell: {@link SpellBuff#getDurationKey(Potion)}, {@link SpellBuff#getStrengthKey(Potion)} * Properties added by this type of spell: {@link SpellBuff#getDurationKey(Potion)}, {@link SpellBuff#getStrengthKey(Potion)}
* <p></p> * <p></p>
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()} * By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
* <p></p> * <p></p>
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()} * By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
* <p></p> * <p></p>
* By default, this type of spell requires a packet to be sent. {@link Spell#requiresPacket()} * By default, this type of spell requires a packet to be sent. {@link Spell#requiresPacket()}
* *
@@ -67,6 +68,7 @@ public class SpellBuff extends Spell {
this.r = r; this.r = r;
this.g = g; this.g = g;
this.b = b; this.b = b;
this.npcSelector((e, o) -> true);
} }
@Override @Override
@@ -103,9 +105,7 @@ public class SpellBuff extends Spell {
return this; return this;
} }
@Override public boolean canBeCastByNPCs(){ return true; } @Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return true; }
@Override public boolean canBeCastByDispensers() { return true; }
@Override @Override
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){

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